1 /* 2 * Copyright (C) 2018 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 17 package com.googlecode.android_scripting.facade.net; 18 19 import android.net.NetworkUtils; 20 import android.os.ParcelFileDescriptor; 21 import android.system.ErrnoException; 22 import android.system.Os; 23 24 import com.googlecode.android_scripting.Log; 25 import com.googlecode.android_scripting.facade.FacadeManager; 26 import com.googlecode.android_scripting.jsonrpc.RpcReceiver; 27 import com.googlecode.android_scripting.rpc.Rpc; 28 29 import java.io.DataInputStream; 30 import java.io.DataOutputStream; 31 import java.io.FileDescriptor; 32 import java.io.IOException; 33 import java.io.InterruptedIOException; 34 import java.io.ObjectInputStream; 35 import java.io.ObjectOutputStream; 36 import java.net.DatagramPacket; 37 import java.net.DatagramSocket; 38 import java.net.InetAddress; 39 import java.net.ServerSocket; 40 import java.net.Socket; 41 import java.net.SocketException; 42 import java.nio.charset.StandardCharsets; 43 import java.util.HashMap; 44 45 /** 46 * This class has APIs for various socket types 47 * android.system.Os socket used for UDP and TCP sockets 48 * java.net.DatagramSocket for UDP sockets 49 * java.net.Socket and java.net.ServerSocket for TCP sockets 50 */ 51 public class SocketFacade extends RpcReceiver { 52 53 public static HashMap<String, DatagramSocket> sDatagramSocketHashMap = 54 new HashMap<String, DatagramSocket>(); 55 private static HashMap<String, Socket> sSocketHashMap = 56 new HashMap<String, Socket>(); 57 private static HashMap<String, ServerSocket> sServerSocketHashMap = 58 new HashMap<String, ServerSocket>(); 59 private static HashMap<String, FileDescriptor> sFileDescriptorHashMap = 60 new HashMap<String, FileDescriptor>(); 61 public static int MAX_BUF_SZ = 2048; 62 public static int SOCK_TIMEOUT = 1500; 63 SocketFacade(FacadeManager manager)64 public SocketFacade(FacadeManager manager) { 65 super(manager); 66 } 67 68 /** 69 * Method to return hash value of a DatagramSocket object 70 * Assumes that a unique hashCode is generated for each object 71 * and odds of equal hashCode for different sockets is negligible 72 */ getDatagramSocketId(DatagramSocket socket)73 private String getDatagramSocketId(DatagramSocket socket) { 74 return "DATAGRAMSOCKET:" + socket.hashCode(); 75 } 76 77 /** 78 * Method to return hash value of a Socket object 79 * Assumes that a unique hashCode is generated for each object 80 * and odds of equal hashCode for different sockets is negligible 81 */ getSocketId(Socket socket)82 private String getSocketId(Socket socket) { 83 return "SOCKET:" + socket.hashCode(); 84 } 85 86 /** 87 * Method to return hash value of a ServerSocket object 88 * Assumes that a unique hashCode is generated for each object 89 * and odds of equal hashCode for different sockets is negligible 90 */ getServerSocketId(ServerSocket socket)91 private String getServerSocketId(ServerSocket socket) { 92 return "SERVERSOCKET:" + socket.hashCode(); 93 } 94 95 /** 96 * Method to return hash value of a FileDescriptor object 97 * Assumes that a unique hashCode is generated for each object 98 * and odds of equal hashCode for different sockets is negligible 99 */ getFileDescriptorId(FileDescriptor fd)100 private String getFileDescriptorId(FileDescriptor fd) { 101 return "FILEDESCRIPTOR:" + fd.hashCode(); 102 } 103 104 /** 105 * Method to retrieve FileDescriptor from hash key 106 * @param id : Hash key of {@link FileDescriptor} 107 * @return FileDescriptor 108 */ getFileDescriptor(String id)109 public static FileDescriptor getFileDescriptor(String id) { 110 return sFileDescriptorHashMap.get(id); 111 } 112 113 /** 114 * Method to retrieve DatagramSocket from hash key 115 * @param datagramSocketId : Hash key of {@link DatagramSocket} 116 * @return DatagramSocket 117 */ getDatagramSocket(String datagramSocketId)118 public static DatagramSocket getDatagramSocket(String datagramSocketId) { 119 return sDatagramSocketHashMap.get(datagramSocketId); 120 } 121 122 /** 123 * Method to retrieve Socket from hash key 124 * @param socketId : Hash key of {@link Socket} 125 * @return Socket 126 */ getSocket(String socketId)127 public static Socket getSocket(String socketId) { 128 return sSocketHashMap.get(socketId); 129 } 130 131 /** 132 * Method to retrieve ServerSocket from hash key 133 * @param serverSocketId : Hash key of {@link ServerSocket} 134 * @return ServerSocket 135 */ getServerSocket(String serverSocketId)136 public static ServerSocket getServerSocket(String serverSocketId) { 137 return sServerSocketHashMap.get(serverSocketId); 138 } 139 140 /* 141 * The following APIs for open, close, send and recv over Stream sockets 142 * This uses java.net.Socket and java.net.ServerSocket and applies only for TCP traffic 143 */ 144 145 /** 146 * Open TCP client socket and connect to server using java.net.Socket 147 * @param remote : IP addr of the server 148 * @param remotePort : Port of the server's socket 149 * @param local : IP addr of the client 150 * @param localPort : Port the client's socket 151 * @return Hash key of {@link Socket} 152 */ 153 @Rpc(description = "Open TCP socket & connect to server") openTcpSocket( String remote, Integer remotePort, String local, Integer localPort)154 public String openTcpSocket( 155 String remote, 156 Integer remotePort, 157 String local, 158 Integer localPort) { 159 try { 160 InetAddress remoteAddr = NetworkUtils.numericToInetAddress(remote); 161 InetAddress localAddr = NetworkUtils.numericToInetAddress(local); 162 Socket socket = new Socket(remoteAddr, remotePort.intValue(), localAddr, 163 localPort.intValue()); 164 socket.setSoLinger(true, 0); 165 String id = getSocketId(socket); 166 sSocketHashMap.put(id, socket); 167 return id; 168 } catch (IOException e) { 169 Log.e("Socket: Failed to open TCP client socket ", e); 170 } 171 return null; 172 } 173 174 /** 175 * Return port number of Socket 176 * @param socketId : Hash key of {@link Socket} 177 * @return Port number in int 178 */ 179 @Rpc(description = "Get port number of a Socket") getPortOfSocket(String socketId)180 public int getPortOfSocket(String socketId) { 181 Socket socket = sSocketHashMap.get(socketId); 182 return socket.getLocalPort(); 183 } 184 185 /** 186 * Close socket of java.net.Socket class 187 * @param socketId : Hash key of {@link Socket} 188 * @return True if closing socket is successful 189 */ 190 @Rpc(description = "Close TCP client socket") closeTcpSocket(String socketId)191 public Boolean closeTcpSocket(String socketId) { 192 Socket socket = sSocketHashMap.get(socketId); 193 try { 194 socket.close(); 195 sSocketHashMap.remove(socketId); 196 return true; 197 } catch (IOException e) { 198 Log.e("Socket: Failed to close TCP client socket ", e); 199 } 200 return false; 201 } 202 203 /** 204 * Open TCP server socket using java.net.ServerSocket 205 * @param addr : IP addr of the server 206 * @param port : Port of the server's socket 207 * @return Hash key of {@link ServerSocket} 208 */ 209 @Rpc(description = "Open TCP server socket and accept connection") openTcpServerSocket(String addr, Integer port)210 public String openTcpServerSocket(String addr, Integer port) { 211 try { 212 InetAddress localAddr = NetworkUtils.numericToInetAddress(addr); 213 ServerSocket serverSocket = new ServerSocket(port.intValue(), 10, localAddr); 214 String id = getServerSocketId(serverSocket); 215 sServerSocketHashMap.put(id, serverSocket); 216 return id; 217 } catch (IOException e) { 218 Log.e("Socket: Failed to open TCP server socket ", e); 219 } 220 return null; 221 } 222 223 /** 224 * Return port number of ServerSocket 225 * @param serverSocketId : Hash key of {@link ServerSocket} 226 * @return Port number in int 227 */ 228 @Rpc(description = "Get port number of a ServerSocket") getPortOfServerSocket(String serverSocketId)229 public int getPortOfServerSocket(String serverSocketId) { 230 ServerSocket socket = sServerSocketHashMap.get(serverSocketId); 231 return socket.getLocalPort(); 232 } 233 234 /** 235 * Close TCP server socket 236 * @param serverSocketId : Hash key of {@link ServerSocket} 237 * @return True if server socket is closed 238 */ 239 @Rpc(description = "Close TCP server socket") closeTcpServerSocket(String serverSocketId)240 public Boolean closeTcpServerSocket(String serverSocketId) { 241 ServerSocket socket = sServerSocketHashMap.get(serverSocketId); 242 try { 243 socket.close(); 244 sServerSocketHashMap.remove(serverSocketId); 245 return true; 246 } catch (IOException e) { 247 Log.e("Socket: Failed to close TCP server socket ", e); 248 } 249 return false; 250 } 251 252 /** 253 * Get FileDescriptor of a Socket object 254 * @param socketId : Hash key of {@link Socket} 255 * @return Filedescriptor object in string 256 */ 257 @Rpc(description = "Get FileDescriptor object of socket") getFileDescriptorOfSocket(String socketId)258 public String getFileDescriptorOfSocket(String socketId) { 259 Socket socket = sSocketHashMap.get(socketId); 260 ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket); 261 FileDescriptor fd = pfd.getFileDescriptor(); 262 String fdId = getFileDescriptorId(fd); 263 sFileDescriptorHashMap.put(fdId, fd); 264 return fdId; 265 } 266 267 /** 268 * Shutdown a FileDescriptor 269 * @param id : Hash key of {@link FileDescriptor} 270 * @return true if shutdown of Filedescriptor is successful, false if not 271 */ 272 @Rpc(description = "Shutdown FileDescriptor") shutdownFileDescriptor(String id)273 public Boolean shutdownFileDescriptor(String id) { 274 try { 275 FileDescriptor fd = sFileDescriptorHashMap.get(id); 276 Os.shutdown(fd, 2); 277 return true; 278 } catch (ErrnoException e) { 279 Log.e("Socket: Failed to shutdown FileDescriptor ", e); 280 } 281 return false; 282 } 283 284 /** 285 * Get the local port on which the ServerSocket is listening. Useful when the server socket 286 * is initialized with 0 port (i.e. selects an available port) 287 * 288 * @param serverSocketId : Hash key of ServerSocket (returned by 289 * {@link #openTcpServerSocket(String, Integer)}. 290 * @return An integer - the port number (0 in case of an error). 291 */ 292 @Rpc(description = "Get the TCP Server socket port number") getTcpServerSocketPort(String serverSocketId)293 public Integer getTcpServerSocketPort(String serverSocketId) { 294 ServerSocket socket = sServerSocketHashMap.get(serverSocketId); 295 return socket.getLocalPort(); 296 } 297 298 /** 299 * Accept TCP connection 300 * @param serverSocketId : Hash key of ServerSocket 301 * @return Hash key of {@link Socket} 302 */ 303 @Rpc(description = "Accept connection") acceptTcpSocket(String serverSocketId)304 public String acceptTcpSocket(String serverSocketId) { 305 try { 306 ServerSocket serverSocket = sServerSocketHashMap.get(serverSocketId); 307 Socket socket = serverSocket.accept(); 308 String sockId = getSocketId(socket); 309 sSocketHashMap.put(sockId, socket); 310 return sockId; 311 } catch (IOException e) { 312 Log.e("Socket: Failed to accept connection ", e); 313 } 314 return null; 315 } 316 317 /** 318 * Send data to server - only ASCII/UTF characters 319 * @param socketId : Hash key of {@link Socket} 320 * @param message : Data to send to in String 321 * @return True if sending data is successful, false if not 322 */ 323 @Rpc(description = "Send data from client") sendDataOverTcpSocket(String socketId, String message)324 public Boolean sendDataOverTcpSocket(String socketId, String message) { 325 Socket socket = sSocketHashMap.get(socketId); 326 try { 327 ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); 328 oos.writeObject(message); 329 return true; 330 } catch (IOException | SecurityException | IllegalArgumentException e) { 331 Log.e("Socket: Failed to send data from socket ", e); 332 } 333 return false; 334 } 335 336 /** 337 * Receive data on ServerSocket - only ASCII/UTF characters 338 * @param serverSocketId : Hash key of {@link ServerSocket} 339 * @return Received data in String, null if unabled to read from socket 340 */ 341 @Rpc(description = "Recv data from client") recvDataOverTcpSocket(String serverSocketId)342 public String recvDataOverTcpSocket(String serverSocketId) { 343 Socket socket = sSocketHashMap.get(serverSocketId); 344 try { 345 ObjectInputStream ois = new ObjectInputStream(socket.getInputStream()); 346 String message = (String) ois.readObject(); 347 Log.d("Socket: Received " + message); 348 return message; 349 } catch (IOException | SecurityException | IllegalArgumentException 350 | ClassNotFoundException e) { 351 Log.e("Socket: Failed to read from socket ", e); 352 } 353 return null; 354 } 355 356 /** 357 * Send data over TCP socket using DataOutputStream 358 * @param socketId : Hash key of {@link Socket} 359 * @param message : Data to send in string 360 * @return true if sending data is successful, false if not 361 */ 362 @Rpc(description = "Send data from client using DataOutputStream") tcpSocketWrite(String socketId, String message)363 public Boolean tcpSocketWrite(String socketId, String message) { 364 Socket socket = sSocketHashMap.get(socketId); 365 try { 366 DataOutputStream out = new DataOutputStream(socket.getOutputStream()); 367 Log.d("Socket: Sending -> " + message); 368 out.write(message.getBytes(), 0, message.length()); 369 out.flush(); 370 return true; 371 } catch (IOException | SecurityException | IllegalArgumentException e) { 372 Log.e("Socket: Failed to read from socket ", e); 373 } 374 return false; 375 } 376 377 /** 378 * Receive data over TCP socket using DataInputStream 379 * @param socketId : Hash key of {@link Socket} 380 * @return String message if received data, null if not 381 */ 382 @Rpc(description = "Recv data from server using DataInputStream") tcpSocketRecv(String socketId)383 public String tcpSocketRecv(String socketId) { 384 Socket socket = sSocketHashMap.get(socketId); 385 try { 386 byte[] messageByte = new byte[1024]; 387 DataInputStream in = new DataInputStream(socket.getInputStream()); 388 int bytesRead = in.read(messageByte); 389 String message = new String(messageByte, 0, bytesRead); 390 Log.d("Socket: Received -> " + message); 391 return message; 392 } catch (IOException | SecurityException | IllegalArgumentException e) { 393 Log.e("Socket: Failed to read from socket ", e); 394 } 395 return null; 396 } 397 398 /* 399 * The following APIs are used to open, close, send and recv data over DatagramSocket 400 * This uses java.net.DatagramSocket class and only applies for UDP 401 */ 402 403 /** 404 * Open datagram socket using java.net.DatagramSocket 405 * @param addr : IP addr to use 406 * @param port : port to open socket on 407 * @return Hash key of {@link DatagramSocket} 408 */ 409 @Rpc(description = "Open datagram socket") openDatagramSocket(String addr, Integer port)410 public String openDatagramSocket(String addr, Integer port) { 411 InetAddress localAddr = NetworkUtils.numericToInetAddress(addr); 412 try { 413 DatagramSocket socket = new DatagramSocket(port.intValue(), localAddr); 414 socket.setSoTimeout(SOCK_TIMEOUT); 415 String id = getDatagramSocketId(socket); 416 sDatagramSocketHashMap.put(id, socket); 417 return id; 418 } catch (SocketException e) { 419 Log.e("Socket: Failed to open datagram socket"); 420 } 421 return null; 422 } 423 424 /** 425 * Return port number of DatagramSocket 426 * @param datagramSocketId : Hash key of {@link DatagramSocket} 427 * @return Port number in int 428 */ 429 @Rpc(description = "Get port number of a DatagramSocket") getPortOfDatagramSocket(String datagramSocketId)430 public int getPortOfDatagramSocket(String datagramSocketId) { 431 DatagramSocket socket = sDatagramSocketHashMap.get(datagramSocketId); 432 return socket.getLocalPort(); 433 } 434 435 /** 436 * Close datagram socket 437 * @param datagramSocketId : Hash key of {@link DatagramSocket} 438 * @return True if close is successful 439 */ 440 @Rpc(description = "Close datagram socket") closeDatagramSocket(String datagramSocketId)441 public Boolean closeDatagramSocket(String datagramSocketId) { 442 DatagramSocket socket = sDatagramSocketHashMap.get(datagramSocketId); 443 socket.close(); 444 sDatagramSocketHashMap.remove(datagramSocketId); 445 return true; 446 } 447 448 /** 449 * Send data from datagram socket to server. 450 * @param datagramSocketId : Hash key of {@link DatagramSocket} 451 * @param message : data to send in String 452 * @param addr : IP addr to send the data to 453 * @param port : port of the socket to send the data to 454 * @return True if sending data is successful 455 */ 456 @Rpc(description = "Send data over socket", returns = "True if sending data successful") sendDataOverDatagramSocket(String datagramSocketId, String message, String addr, Integer port)457 public Boolean sendDataOverDatagramSocket(String datagramSocketId, String message, String addr, 458 Integer port) { 459 byte[] buf = message.getBytes(); 460 try { 461 InetAddress remoteAddr = NetworkUtils.numericToInetAddress(addr); 462 DatagramSocket socket = sDatagramSocketHashMap.get(datagramSocketId); 463 DatagramPacket pkt = new DatagramPacket(buf, buf.length, remoteAddr, port.intValue()); 464 socket.send(pkt); 465 return true; 466 } catch (IOException e) { 467 Log.e("Socket: Failed to send data over datagram socket"); 468 } 469 return false; 470 } 471 472 /** 473 * Receive data on the datagram socket 474 * @param datagramSocketId : Hash key of {@link DatagramSocket} 475 * @return Received data in String format 476 */ 477 @Rpc(description = "Receive data over socket", returns = "Received data in String") recvDataOverDatagramSocket(String datagramSocketId)478 public String recvDataOverDatagramSocket(String datagramSocketId) { 479 byte[] buf = new byte[MAX_BUF_SZ]; 480 try { 481 DatagramSocket socket = sDatagramSocketHashMap.get(datagramSocketId); 482 DatagramPacket dgramPacket = new DatagramPacket(buf, MAX_BUF_SZ); 483 socket.receive(dgramPacket); 484 return new String(dgramPacket.getData(), 0, dgramPacket.getLength()); 485 } catch (IOException e) { 486 Log.e("Socket: Failed to recv data over datagram socket"); 487 } 488 return null; 489 } 490 491 /* 492 * The following APIs are used to open, close, send and receive data over Os.socket 493 * This uses android.system.Os class and can be used for UDP and TCP traffic 494 */ 495 496 /** 497 * Open socket using android.system.Os class. 498 * @param domain : protocol family. Ex: IPv4 or IPv6 499 * @param type : socket type. Ex: DGRAM or STREAM 500 * @param addr : IP addr to use 501 * @param port : port to open socket on 502 * @return Hash key of {@link FileDescriptor} 503 */ 504 @Rpc(description = "Open socket") openSocket(Integer domain, Integer type, String addr, Integer port)505 public String openSocket(Integer domain, Integer type, String addr, Integer port) { 506 try { 507 FileDescriptor fd = Os.socket(domain, type, 0); 508 InetAddress localAddr = NetworkUtils.numericToInetAddress(addr); 509 Os.bind(fd, localAddr, port.intValue()); 510 String id = getFileDescriptorId(fd); 511 sFileDescriptorHashMap.put(id, fd); 512 return id; 513 } catch (SocketException | ErrnoException e) { 514 Log.e("Socket: Failed to open socket ", e); 515 } 516 return null; 517 } 518 519 /** 520 * Close socket of android.system.Os class 521 * @param id : Hash key of {@link FileDescriptor} 522 * @return True if connect successful 523 */ 524 @Rpc(description = "Close socket") closeSocket(String id)525 public Boolean closeSocket(String id) { 526 FileDescriptor fd = sFileDescriptorHashMap.get(id); 527 try { 528 Os.close(fd); 529 return true; 530 } catch (ErrnoException e) { 531 Log.e("Socket: Failed to close socket ", e); 532 } 533 return false; 534 } 535 536 /** 537 * Send data from the socket 538 * @param remoteAddr : IP addr to send the data to 539 * @param remotePort : Port of the socket to send the data to 540 * @param message : data to send in String 541 * @param id : Hash key of {@link FileDescriptor} 542 * @return True if connect successful 543 */ 544 @Rpc(description = "Send data to server") sendDataOverSocket( String remoteAddr, Integer remotePort, String message, String id)545 public Boolean sendDataOverSocket( 546 String remoteAddr, Integer remotePort, String message, String id) { 547 FileDescriptor fd = sFileDescriptorHashMap.get(id); 548 InetAddress remote = NetworkUtils.numericToInetAddress(remoteAddr); 549 try { 550 byte [] data = new String(message).getBytes(StandardCharsets.UTF_8); 551 int bytes = Os.sendto(fd, data, 0, data.length, 0, remote, remotePort.intValue()); 552 Log.d("Socket: Sent " + String.valueOf(bytes) + " bytes"); 553 return true; 554 } catch (ErrnoException | SocketException e) { 555 Log.e("Socket: Sending data over socket failed ", e); 556 } 557 return false; 558 } 559 560 /** 561 * Receive data on the socket. 562 * @param id : Hash key of {@link FileDescriptor} 563 * @return Received data in String format 564 */ 565 @Rpc(description = "Recv data on server") recvDataOverSocket(String id)566 public String recvDataOverSocket(String id) { 567 byte[] data = new byte[MAX_BUF_SZ]; 568 FileDescriptor fd = sFileDescriptorHashMap.get(id); 569 try { 570 Os.read(fd, data, 0, data.length); 571 return new String(data, StandardCharsets.UTF_8); 572 } catch (ErrnoException | InterruptedIOException e) { 573 Log.e("Socket: Receiving data over socket failed ", e); 574 } 575 return null; 576 } 577 578 /** 579 * Listen for connections from client. 580 * @param id : Hash key of {@link FileDescriptor} 581 * @return True if listen successful 582 */ 583 @Rpc(description = "Listen for connection on server") listenSocket(String id)584 public Boolean listenSocket(String id) { 585 FileDescriptor fd = sFileDescriptorHashMap.get(id); 586 try { 587 // Start listening with buffer of size 10 588 Os.listen(fd, 10); 589 return true; 590 } catch (ErrnoException e) { 591 Log.e("Socket: Failed to listen on socket ", e); 592 } 593 return false; 594 } 595 596 /** 597 * TCP connect to server from client. 598 * @param id : Hash key of {@link FileDescriptor} 599 * @param addr : IP addr in string of server 600 * @param port : server's port to connect to 601 * @return True if connect successful 602 */ 603 @Rpc(description = "Connect to server") connectSocket(String id, String addr, Integer port)604 public Boolean connectSocket(String id, String addr, Integer port) { 605 FileDescriptor fd = sFileDescriptorHashMap.get(id); 606 try { 607 InetAddress remoteAddr = NetworkUtils.numericToInetAddress(addr); 608 Os.connect(fd, remoteAddr, port.intValue()); 609 return true; 610 } catch (SocketException | ErrnoException e) { 611 Log.e("Socket: Failed to connect socket ", e); 612 } 613 return false; 614 } 615 616 /** 617 * Accept TCP connection from the client to server. 618 * @param id : Hash key of server {@link FileDescriptor} 619 * @return Hash key of FileDescriptor returned by successful accept() 620 */ 621 @Rpc(description = "Accept connection on server") acceptSocket(String id)622 public String acceptSocket(String id) { 623 FileDescriptor fd = sFileDescriptorHashMap.get(id); 624 try { 625 FileDescriptor socket = Os.accept(fd, null); 626 String socketId = getFileDescriptorId(socket); 627 sFileDescriptorHashMap.put(socketId, socket); 628 return socketId; 629 } catch (SocketException | ErrnoException e) { 630 Log.e("Socket: Failed to accept on socket ", e); 631 } 632 return null; 633 } 634 635 @Override shutdown()636 public void shutdown() {} 637 } 638