1 /* 2 * Copyright (C) 2024 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 package com.google.snippet.wifi.aware; 17 18 import android.content.Context; 19 import android.net.ConnectivityManager; 20 import android.net.Network; 21 import android.net.NetworkCapabilities; 22 import android.net.LinkProperties; 23 import android.net.NetworkRequest; 24 import android.net.TransportInfo; 25 import android.net.wifi.aware.WifiAwareChannelInfo; 26 import android.net.wifi.aware.WifiAwareNetworkInfo; 27 28 import androidx.annotation.NonNull; 29 import androidx.test.core.app.ApplicationProvider; 30 31 import com.google.android.mobly.snippet.Snippet; 32 import com.google.android.mobly.snippet.event.EventCache; 33 import com.google.android.mobly.snippet.event.SnippetEvent; 34 import com.google.android.mobly.snippet.rpc.AsyncRpc; 35 import com.google.android.mobly.snippet.rpc.Rpc; 36 import com.google.android.mobly.snippet.util.Log; 37 38 import org.json.JSONException; 39 40 import java.io.IOException; 41 import java.io.InputStream; 42 import java.io.OutputStream; 43 import java.net.Inet6Address; 44 import java.net.InetAddress; 45 import java.net.NetworkInterface; 46 import java.net.ServerSocket; 47 import java.net.SocketException; 48 import java.net.Socket; 49 import java.nio.charset.StandardCharsets; 50 import java.util.ArrayList; 51 import java.util.List; 52 import java.util.concurrent.ConcurrentHashMap; 53 import java.util.Enumeration; 54 55 public class ConnectivityManagerSnippet implements Snippet { 56 private static final String EVENT_KEY_CB_NAME = "callbackName"; 57 private static final String EVENT_KEY_NETWORK = "network"; 58 private static final String EVENT_KEY_NETWORK_CAP = "networkCapabilities"; 59 private static final String EVENT_KEY_NETWORK_INTERFACE ="interfaceName"; 60 private static final String EVENT_KEY_TRANSPORT_INFO_CLASS = "transportInfoClassName"; 61 private static final String EVENT_KEY_TRANSPORT_INFO_CHANNEL_IN_MHZ = "channelInMhz"; 62 private static final int CLOSE_SOCKET_TIMEOUT = 15 * 1000; 63 private static final int ACCEPT_TIMEOUT = 30 * 1000; 64 private static final int SOCKET_SO_TIMEOUT = 30 * 1000; 65 private static final int TRANSPORT_PROTOCOL_TCP = 6; 66 67 private final Context mContext; 68 private final ConnectivityManager mConnectivityManager; 69 70 private final ConcurrentHashMap<String, ServerSocket> mServerSockets = 71 new ConcurrentHashMap<>(); 72 private final ConcurrentHashMap<String, NetworkCallback> mNetworkCallBacks = 73 new ConcurrentHashMap<>(); 74 private final ConcurrentHashMap<String, Socket> mSockets = new ConcurrentHashMap<>(); 75 private final ConcurrentHashMap<String, OutputStream> mOutputStreams = 76 new ConcurrentHashMap<>(); 77 private final ConcurrentHashMap<String, InputStream> mInputStreams = new ConcurrentHashMap<>(); 78 private final ConcurrentHashMap<String, Thread> mSocketThreads = new ConcurrentHashMap<>(); 79 80 /** 81 * Custom exception class for handling specific errors related to the ConnectivityManagerSnippet 82 * operations. 83 */ 84 class ConnectivityManagerSnippetException extends Exception { ConnectivityManagerSnippetException(String msg)85 ConnectivityManagerSnippetException(String msg) { 86 super(msg); 87 } 88 } 89 ConnectivityManagerSnippet()90 public ConnectivityManagerSnippet() throws ConnectivityManagerSnippetException { 91 mContext = ApplicationProvider.getApplicationContext(); 92 mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); 93 if (mConnectivityManager == null) { 94 throw new ConnectivityManagerSnippetException( 95 "ConnectivityManager not " + "available."); 96 } 97 } 98 99 public class NetworkCallback extends ConnectivityManager.NetworkCallback { 100 101 102 String mCallBackId; 103 Network mNetWork; 104 NetworkCapabilities mNetworkCapabilities; 105 106 NetworkCallback(String callBackId)107 NetworkCallback(String callBackId) { 108 mCallBackId = callBackId; 109 } 110 111 @Override onUnavailable()112 public void onUnavailable() { 113 SnippetEvent event = new SnippetEvent(mCallBackId, "NetworkCallback"); 114 event.getData().putString(EVENT_KEY_CB_NAME, "onUnavailable"); 115 EventCache.getInstance().postEvent(event); 116 } 117 118 @Override onCapabilitiesChanged(@onNull Network network, @NonNull NetworkCapabilities networkCapabilities)119 public void onCapabilitiesChanged(@NonNull Network network, 120 @NonNull NetworkCapabilities networkCapabilities) { 121 SnippetEvent event = new SnippetEvent(mCallBackId, "NetworkCallback"); 122 event.getData().putString(EVENT_KEY_CB_NAME, "onCapabilitiesChanged"); 123 event.getData().putParcelable(EVENT_KEY_NETWORK, network); 124 event.getData().putParcelable(EVENT_KEY_NETWORK_CAP, networkCapabilities); 125 mNetWork = network; 126 mNetworkCapabilities = networkCapabilities; 127 TransportInfo transportInfo = networkCapabilities.getTransportInfo(); 128 String transportInfoClassName = ""; 129 if (transportInfo != null) { 130 transportInfoClassName = transportInfo.getClass().getName(); 131 event.getData().putString(EVENT_KEY_TRANSPORT_INFO_CLASS, transportInfoClassName); 132 } 133 if (networkCapabilities.getTransportInfo() instanceof WifiAwareNetworkInfo) { 134 WifiAwareNetworkInfo 135 newWorkInfo = 136 (WifiAwareNetworkInfo) networkCapabilities.getTransportInfo(); 137 List<WifiAwareChannelInfo> channelInfoList = newWorkInfo.getChannelInfoList(); 138 ArrayList<Integer> channelFrequencies = new ArrayList<>(); 139 if (!channelInfoList.isEmpty()) { 140 for (WifiAwareChannelInfo info : channelInfoList) { 141 channelFrequencies.add(info.getChannelFrequencyMhz()); 142 } 143 } 144 event.getData().putIntegerArrayList( 145 EVENT_KEY_TRANSPORT_INFO_CHANNEL_IN_MHZ, channelFrequencies 146 ); 147 String ipv6 = newWorkInfo.getPeerIpv6Addr().toString(); 148 if (ipv6.charAt(0) == '/') { 149 ipv6 = ipv6.substring(1); 150 } 151 event.getData().putString("aware_ipv6", ipv6); 152 int port = newWorkInfo.getPort(); 153 if (port != 0) { 154 event.getData().putInt("port", port); 155 } 156 if (newWorkInfo.getTransportProtocol() != -1) { 157 event.getData().putInt("aware_transport_protocol", 158 newWorkInfo.getTransportProtocol()); 159 } 160 } 161 EventCache.getInstance().postEvent(event); 162 } 163 164 @Override onLinkPropertiesChanged(Network network, LinkProperties linkProperties)165 public void onLinkPropertiesChanged(Network network, 166 LinkProperties linkProperties) { 167 Log.v("NetworkCallback onLinkPropertiesChanged"); 168 SnippetEvent event = new SnippetEvent(mCallBackId, "NetworkCallback"); 169 event.getData().putString(EVENT_KEY_CB_NAME, "onLinkPropertiesChanged"); 170 event.getData().putParcelable(EVENT_KEY_NETWORK, network); 171 event.getData().putString(EVENT_KEY_NETWORK_INTERFACE, 172 linkProperties.getInterfaceName()); 173 EventCache.getInstance().postEvent(event); 174 } 175 176 @Override onLost(@onNull Network network)177 public void onLost(@NonNull Network network) { 178 Log.v("Network onLost"); 179 SnippetEvent event = new SnippetEvent(mCallBackId, "CallbackLost"); 180 event.getData().putString(EVENT_KEY_CB_NAME, "Lost"); 181 event.getData().putParcelable(EVENT_KEY_NETWORK, network); 182 EventCache.getInstance().postEvent(event); 183 } 184 } 185 getInetAddrsForInterface(String ifaceName)186 private Enumeration<InetAddress> getInetAddrsForInterface(String ifaceName) { 187 NetworkInterface iface = null; 188 try { 189 iface = NetworkInterface.getByName(ifaceName); 190 } catch (SocketException e) { 191 return null; 192 } 193 194 if (iface == null) 195 return null; 196 return iface.getInetAddresses(); 197 } 198 /** 199 * Returns the link local IPv6 address of the interface. 200 * 201 * @param ifaceName network interface name. 202 */ 203 @Rpc(description = "Returns the link local IPv6 address of the interface.") connectivityGetLinkLocalIpv6Address(String ifaceName)204 public String connectivityGetLinkLocalIpv6Address(String ifaceName) { 205 Inet6Address inet6Address = null; 206 Enumeration<InetAddress> inetAddresses = getInetAddrsForInterface(ifaceName); 207 if (inetAddresses == null) { 208 return null; 209 } 210 211 while (inetAddresses.hasMoreElements()) { 212 InetAddress addr = inetAddresses.nextElement(); 213 if (addr instanceof Inet6Address) { 214 if (((Inet6Address) addr).isLinkLocalAddress()) { 215 inet6Address = (Inet6Address) addr; 216 break; 217 } 218 } 219 } 220 221 if (inet6Address == null) { 222 return null; 223 } 224 225 return inet6Address.getHostAddress(); 226 } 227 /** 228 * Requests a network with the specified network request and sets a callback for network 229 * events. 230 * 231 * @param callBackId A unique identifier assigned automatically by Mobly. This is 232 * used as the request ID for further operations and event 233 * handling. 234 * @param request The NetworkRequest object that specifies the desired network 235 * characteristics. 236 * @param requestNetWorkId A unique ID to support managing multiple network sessions. 237 * @param requestNetworkTimeoutMs The timeout period (in milliseconds) after which the network 238 * request will expire if no suitable network is found. 239 */ 240 @AsyncRpc(description = "Request a network.") connectivityRequestNetwork(String callBackId, String requestNetWorkId, NetworkRequest request, int requestNetworkTimeoutMs)241 public void connectivityRequestNetwork(String callBackId, String requestNetWorkId, 242 NetworkRequest request, int requestNetworkTimeoutMs) { 243 Log.v("Requesting network with request: " + request.toString()); 244 NetworkCallback callback = new NetworkCallback(callBackId); 245 mNetworkCallBacks.put(requestNetWorkId, callback); 246 mConnectivityManager.requestNetwork(request, callback, requestNetworkTimeoutMs); 247 } 248 249 /** 250 * Unregisters the registered network callback and possibly releases requested networks. 251 * 252 * @param requestId Id of the network request. 253 */ 254 @Rpc(description = "Unregister a network request") connectivityUnregisterNetwork(String requestId)255 public void connectivityUnregisterNetwork(String requestId) { 256 NetworkCallback callback = mNetworkCallBacks.get(requestId); 257 if (callback == null) { 258 return; 259 } 260 if (mConnectivityManager == null) { 261 return; 262 } 263 mConnectivityManager.unregisterNetworkCallback(callback); 264 } 265 266 /** 267 * Starts a server socket on a random available port and waits for incoming connections. A 268 * separate thread is started to handle the socket accept operation asynchronously. The accepted 269 * socket is stored and used for further communication (read/write). 270 * 271 * @param callbackId A unique identifier assigned automatically by Mobly to track the event and 272 * response. 273 * @return The port number assigned by the local system. 274 */ 275 @AsyncRpc(description = "Start a server socket to accept incoming connections.") connectivityServerSocketAccept(String callbackId)276 public int connectivityServerSocketAccept(String callbackId) 277 throws ConnectivityManagerSnippetException, IOException { 278 if (mServerSockets.containsKey(callbackId) && mServerSockets.get(callbackId) != null) { 279 throw new ConnectivityManagerSnippetException("Server socket is already created."); 280 } 281 ServerSocket serverSocket = new ServerSocket(0); 282 int localPort = serverSocket.getLocalPort(); 283 mServerSockets.put(callbackId, serverSocket); 284 // https://developer.callbackId.com/reference/java/net/ServerSocket#setSoTimeout(int) 285 // A call to accept() for this ServerSocket will block for only this amount of time. 286 serverSocket.setSoTimeout(ACCEPT_TIMEOUT); 287 if (mSocketThreads.get(callbackId) != null) { 288 throw new ConnectivityManagerSnippetException( 289 "Server socket thread is already running."); 290 } 291 Thread socketThread = new Thread(() -> { 292 try { 293 Socket tempSocket = mServerSockets.get(callbackId).accept(); 294 mSockets.put(callbackId, tempSocket); 295 mInputStreams.put(callbackId, tempSocket.getInputStream()); 296 mOutputStreams.put(callbackId, tempSocket.getOutputStream()); 297 SnippetEvent event = new SnippetEvent(callbackId, "ServerSocketAccept"); 298 event.getData().putBoolean("isAccept", true); 299 EventCache.getInstance().postEvent(event); 300 } catch (IOException e) { 301 Log.e("Socket accept error", e); 302 SnippetEvent event = new SnippetEvent(callbackId, "ServerSocketAccept"); 303 event.getData().putBoolean("isAccept", false); 304 event.getData().putString("error", e.getMessage()); 305 EventCache.getInstance().postEvent(event); 306 } 307 }); 308 mSocketThreads.put(callbackId, socketThread); 309 socketThread.start(); 310 return localPort; 311 } 312 313 /** 314 * Check if the server socket thread is alive. 315 * 316 * @param sessionId To support multiple network requests happening simultaneously 317 * @return True if the server socket thread is alive. 318 */ connectivityIsSocketThreadAlive(String sessionId)319 public boolean connectivityIsSocketThreadAlive(String sessionId) { 320 Thread thread = mSocketThreads.get(sessionId); 321 if (thread != null) { 322 return thread.isAlive(); 323 } else { 324 return false; 325 } 326 } 327 328 /** 329 * Stops the server socket thread if it's running. 330 * 331 * @param sessionId To support multiple network requests happening simultaneously 332 */ 333 @Rpc(description = "Stop the server socket thread if it's running.") connectivityStopAcceptThread(String sessionId)334 public void connectivityStopAcceptThread(String sessionId) throws IOException { 335 if (connectivityIsSocketThreadAlive(sessionId)) { 336 Thread thread = mSocketThreads.get(sessionId); 337 338 try { 339 connectivityCloseServerSocket(sessionId); 340 thread.join(CLOSE_SOCKET_TIMEOUT); // Wait for the thread to terminate 341 if (thread.isAlive()) { 342 throw new RuntimeException("Server socket thread did not terminate in time"); 343 } 344 } catch (InterruptedException e) { 345 throw new RuntimeException("Error stopping server socket thread", e); 346 } finally { 347 connectivityCloseSocket(sessionId); 348 mSocketThreads.remove(sessionId); 349 } 350 } else { 351 connectivityCloseSocket(sessionId); 352 mSocketThreads.remove(sessionId); 353 } 354 } 355 356 /** 357 * Reads from a socket. 358 * 359 * @param sessionId To support multiple network requests happening simultaneously 360 * @param len The number of bytes to read. 361 */ 362 @Rpc(description = "Reads from a socket.") connectivityReadSocket(String sessionId, int len)363 public String connectivityReadSocket(String sessionId, int len) 364 throws ConnectivityManagerSnippetException, JSONException, IOException { 365 checkInputStream(sessionId); 366 // Read the specified number of bytes from the input stream 367 byte[] buffer = new byte[len]; 368 InputStream inputStream = mInputStreams.get(sessionId); 369 int bytesReadLength = inputStream.read(buffer, 0, len); // Read up to len bytes 370 if (bytesReadLength == -1) { // End of stream reached unexpectedly 371 throw new ConnectivityManagerSnippetException( 372 "End of stream reached before reading expected bytes."); 373 } 374 // Convert the bytes read to a String 375 String receiveStrMsg = new String(buffer, 0, bytesReadLength, StandardCharsets.UTF_8); 376 return receiveStrMsg; 377 } 378 379 /** 380 * Writes to a socket. 381 * 382 * @param sessionId To support multiple network requests happening simultaneously 383 * @param message The message to send. 384 * @throws ConnectivityManagerSnippetException 385 */ 386 @Rpc(description = "Writes to a socket.") connectivityWriteSocket(String sessionId, String message)387 public Boolean connectivityWriteSocket(String sessionId, String message) 388 throws ConnectivityManagerSnippetException, IOException { 389 checkOutputStream(sessionId); 390 byte[] bytes = message.getBytes(StandardCharsets.UTF_8); 391 // Write the message to the output stream 392 OutputStream outputStream = mOutputStreams.get(sessionId); 393 outputStream.write(bytes, 0, bytes.length); 394 outputStream.flush(); 395 return true; 396 397 398 } 399 400 /** 401 * Closes the socket. 402 * 403 * @param sessionId To support multiple network requests happening simultaneously 404 * @throws ConnectivityManagerSnippetException 405 */ connectivityCloseSocket(String sessionId)406 public void connectivityCloseSocket(String sessionId) throws IOException { 407 Socket socket = mSockets.get(sessionId); 408 if (socket != null && !socket.isClosed()) { 409 socket.close(); 410 } 411 mSockets.remove(sessionId); 412 413 } 414 415 /** 416 * Closes the server socket. 417 * 418 * @param sessionId To support multiple network requests happening simultaneously 419 * @throws IOException 420 */ connectivityCloseServerSocket(String sessionId)421 public void connectivityCloseServerSocket(String sessionId) throws IOException { 422 ServerSocket serverSocket = mServerSockets.get(sessionId); 423 if (serverSocket != null && !serverSocket.isClosed()) { 424 serverSocket.close(); 425 } 426 mServerSockets.remove(sessionId); 427 } 428 429 /** 430 * Closes the outputStream. 431 * 432 * @throws ConnectivityManagerSnippetException 433 */ 434 @Rpc(description = "Close the outputStream.") connectivityCloseWrite(String sessionId)435 public void connectivityCloseWrite(String sessionId) 436 throws IOException, ConnectivityManagerSnippetException { 437 OutputStream outputStream = mOutputStreams.get(sessionId); 438 if (outputStream != null) { 439 outputStream.close(); 440 } 441 mOutputStreams.remove(sessionId); 442 443 444 } 445 446 /** 447 * Closes the inputStream. 448 * 449 * @throws ConnectivityManagerSnippetException 450 */ 451 @Rpc(description = "Close the inputStream.") connectivityCloseRead(String sessionId)452 public void connectivityCloseRead(String sessionId) 453 throws IOException, ConnectivityManagerSnippetException { 454 InputStream inputStream = mInputStreams.get(sessionId); 455 if (inputStream != null) { 456 inputStream.close(); 457 } 458 mInputStreams.remove(sessionId); 459 } 460 checkOutputStream(String sessionId)461 private void checkOutputStream(String sessionId) throws ConnectivityManagerSnippetException { 462 OutputStream outputStream = mOutputStreams.get(sessionId); 463 if (outputStream == null) { 464 throw new ConnectivityManagerSnippetException("Output stream is not created.Please " 465 + "call connectivityCreateSocketOverWiFiAware() or " 466 + "connectivityServerSocketAccept() first."); 467 } 468 } 469 checkInputStream(String sessionId)470 private void checkInputStream(String sessionId) throws ConnectivityManagerSnippetException { 471 InputStream inputStream = mInputStreams.get(sessionId); 472 if (inputStream == null) { 473 throw new ConnectivityManagerSnippetException("Input stream is not created.Please " 474 + "call connectivityCreateSocketOverWiFiAware() or " 475 + "connectivityServerSocketAccept() first."); 476 } 477 } 478 479 /** 480 * Creates a socket using Wi-Fi Aware's peer-to-peer connection capabilities. Only TCP transport 481 * protocol is supported. The method uses the session ID to track and manage the socket. 482 * 483 * @param sessionId A unique ID to manage multiple network requests simultaneously. 484 * @param peerLocalPort The port number of the peer device. 485 */ 486 @Rpc(description = "Create to a socket.") connectivityCreateSocketOverWiFiAware(String sessionId, int peerLocalPort)487 public void connectivityCreateSocketOverWiFiAware(String sessionId, int peerLocalPort) 488 throws ConnectivityManagerSnippetException, IOException { 489 NetworkCallback netWorkCallBackBySessionId = getNetWorkCallbackBySessionId(sessionId); 490 NetworkCapabilities networkCapabilities = netWorkCallBackBySessionId.mNetworkCapabilities; 491 Network netWork = netWorkCallBackBySessionId.mNetWork; 492 checkNetworkCapabilities(networkCapabilities); 493 checkNetwork(netWork); 494 Socket socket = mSockets.get(sessionId); 495 if (socket != null) { 496 throw new ConnectivityManagerSnippetException("Socket is already created" 497 + ".Please call connectivityCloseSocket(String sessionId) or " 498 + "connectivityStopAcceptThread" + "(String sessionId) " + "to release first."); 499 } 500 501 checkNetworkCapabilities(networkCapabilities); 502 WifiAwareNetworkInfo peerAwareInfo = 503 (WifiAwareNetworkInfo) networkCapabilities.getTransportInfo(); 504 if (peerAwareInfo == null) { 505 throw new ConnectivityManagerSnippetException("PeerAwareInfo is null."); 506 } 507 int peerPort = peerAwareInfo.getPort(); 508 Inet6Address peerIpv6Addr = peerAwareInfo.getPeerIpv6Addr(); 509 if (peerPort == 0) { 510 peerPort = peerLocalPort; 511 if (peerPort == 0) { 512 throw new ConnectivityManagerSnippetException("Invalid port number."); 513 } 514 } else { 515 516 int transportProtocol = peerAwareInfo.getTransportProtocol(); 517 if (transportProtocol != TRANSPORT_PROTOCOL_TCP) { 518 throw new ConnectivityManagerSnippetException( 519 "Only support TCP transport protocol."); 520 } 521 } 522 523 524 Socket createSocket = netWork.getSocketFactory().createSocket(peerIpv6Addr, peerPort); 525 createSocket.setSoTimeout(SOCKET_SO_TIMEOUT); 526 mSockets.put(sessionId, createSocket); 527 mInputStreams.put(sessionId, createSocket.getInputStream()); 528 mOutputStreams.put(sessionId, createSocket.getOutputStream()); 529 } 530 531 getNetWorkCallbackBySessionId(String sessionId)532 private NetworkCallback getNetWorkCallbackBySessionId(String sessionId) 533 throws ConnectivityManagerSnippetException { 534 NetworkCallback callback = mNetworkCallBacks.get(sessionId); 535 if (callback == null) { 536 throw new ConnectivityManagerSnippetException("Network callback is not created.Please " 537 + "call connectivityRequestNetwork() first."); 538 539 } 540 return callback; 541 } 542 543 /** 544 * Check if the network capabilities is created. 545 * 546 * @throws ConnectivityManagerSnippetException 547 */ checkNetworkCapabilities(NetworkCapabilities networkCapabilities)548 private void checkNetworkCapabilities(NetworkCapabilities networkCapabilities) 549 throws ConnectivityManagerSnippetException { 550 if (networkCapabilities == null) { 551 throw new ConnectivityManagerSnippetException("Network capabilities is not created."); 552 } 553 } 554 555 /** 556 * Check if the network is created. 557 * 558 * @throws ConnectivityManagerSnippetException 559 */ checkNetwork(Network network)560 private void checkNetwork(Network network) throws ConnectivityManagerSnippetException { 561 if (network == null) { 562 throw new ConnectivityManagerSnippetException("Network is not created."); 563 } 564 } 565 566 /** 567 * Check if the server socket is created. 568 * 569 * @throws ConnectivityManagerSnippetException 570 */ checkServerSocket(String sessionId)571 private void checkServerSocket(String sessionId) throws ConnectivityManagerSnippetException { 572 if (mServerSockets.get(sessionId) == null) { 573 throw new ConnectivityManagerSnippetException("Server socket is not created" 574 + ".Please call connectivityInitServerSocket() first."); 575 } 576 } 577 578 /** 579 * Close all sockets. 580 * 581 * @param sessionId To support multiple network requests happening simultaneously 582 * @throws IOException 583 */ 584 @Rpc(description = "Close all sockets.") connectivityCloseAllSocket(String sessionId)585 public void connectivityCloseAllSocket(String sessionId) 586 throws IOException, ConnectivityManagerSnippetException { 587 connectivityStopAcceptThread(sessionId); 588 connectivityCloseServerSocket(sessionId); 589 connectivityCloseRead(sessionId); 590 connectivityCloseWrite(sessionId); 591 } 592 593 @Override shutdown()594 public void shutdown() throws Exception { 595 try { 596 for (NetworkCallback callback : mNetworkCallBacks.values()) { 597 mConnectivityManager.unregisterNetworkCallback(callback); 598 } 599 mNetworkCallBacks.clear(); 600 601 } catch (Exception e) { 602 Log.e("Error unregistering network callback", e); 603 } 604 try { 605 connectivityReleaseAllSockets(); 606 } catch (Exception e) { 607 Log.e("Error closing sockets", e); 608 } 609 Snippet.super.shutdown(); 610 } 611 612 /** 613 * Close all sockets. 614 * 615 * @throws IOException 616 */ 617 @Rpc(description = "Close all sockets.") connectivityReleaseAllSockets()618 public void connectivityReleaseAllSockets() { 619 for (Socket socket : mSockets.values()) { 620 try { 621 if (socket != null && !socket.isClosed()) { 622 socket.close(); 623 } 624 } catch (IOException e) { 625 Log.e("Error closing socket", e); 626 } 627 } 628 mSockets.clear(); 629 for (ServerSocket serverSocket : mServerSockets.values()) { 630 try { 631 if (serverSocket != null && !serverSocket.isClosed()) { 632 serverSocket.close(); 633 } 634 } catch (IOException e) { 635 Log.e("Error closing server socket", e); 636 } 637 } 638 mServerSockets.clear(); 639 for (OutputStream outputStream : mOutputStreams.values()) { 640 try { 641 if (outputStream != null) { 642 outputStream.close(); 643 } 644 } catch (IOException e) { 645 Log.e("Error closing output stream", e); 646 } 647 } 648 mOutputStreams.clear(); 649 for (InputStream inputStream : mInputStreams.values()) { 650 try { 651 if (inputStream != null) { 652 inputStream.close(); 653 } 654 } catch (IOException e) { 655 Log.e("Error closing input stream", e); 656 } 657 } 658 mInputStreams.clear(); 659 } 660 } 661