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 17 package com.android.server; 18 19 import static android.content.pm.PackageManager.FEATURE_BLUETOOTH_LE; 20 import static android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_ANY; 21 import static android.net.L2capNetworkSpecifier.ROLE_CLIENT; 22 import static android.net.L2capNetworkSpecifier.ROLE_SERVER; 23 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED; 24 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; 25 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; 26 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; 27 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; 28 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; 29 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; 30 import static android.net.NetworkCapabilities.RES_ID_MATCH_ALL_RESERVATIONS; 31 import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; 32 import static android.system.OsConstants.F_GETFL; 33 import static android.system.OsConstants.F_SETFL; 34 import static android.system.OsConstants.O_NONBLOCK; 35 36 import android.annotation.Nullable; 37 import android.bluetooth.BluetoothAdapter; 38 import android.bluetooth.BluetoothDevice; 39 import android.bluetooth.BluetoothManager; 40 import android.bluetooth.BluetoothServerSocket; 41 import android.bluetooth.BluetoothSocket; 42 import android.content.Context; 43 import android.content.pm.PackageManager; 44 import android.net.ConnectivityManager; 45 import android.net.L2capNetworkSpecifier; 46 import android.net.NetworkCapabilities; 47 import android.net.NetworkProvider; 48 import android.net.NetworkProvider.NetworkOfferCallback; 49 import android.net.NetworkRequest; 50 import android.net.NetworkScore; 51 import android.os.Handler; 52 import android.os.HandlerThread; 53 import android.os.ParcelFileDescriptor; 54 import android.system.Os; 55 import android.util.ArrayMap; 56 import android.util.ArraySet; 57 import android.util.Log; 58 59 import com.android.internal.annotations.VisibleForTesting; 60 import com.android.net.module.util.HandlerUtils; 61 import com.android.net.module.util.ServiceConnectivityJni; 62 import com.android.server.net.L2capNetwork; 63 import com.android.server.net.L2capNetwork.L2capIpClient; 64 import com.android.server.net.L2capPacketForwarder; 65 66 import java.io.IOException; 67 import java.util.ArrayList; 68 import java.util.List; 69 import java.util.Map; 70 import java.util.Set; 71 72 73 public class L2capNetworkProvider { 74 private static final String TAG = L2capNetworkProvider.class.getSimpleName(); 75 private static final NetworkCapabilities COMMON_CAPABILITIES = 76 // TODO: add NET_CAPABILITY_NOT_RESTRICTED and check that getRequestorUid() has 77 // BLUETOOTH_CONNECT permission. 78 NetworkCapabilities.Builder.withoutDefaultCapabilities() 79 .addTransportType(TRANSPORT_BLUETOOTH) 80 .addCapability(NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED) 81 .addCapability(NET_CAPABILITY_NOT_CONGESTED) 82 .addCapability(NET_CAPABILITY_NOT_METERED) 83 .addCapability(NET_CAPABILITY_NOT_ROAMING) 84 .addCapability(NET_CAPABILITY_NOT_SUSPENDED) 85 .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) 86 .addCapability(NET_CAPABILITY_NOT_VPN) 87 .build(); 88 private final Dependencies mDeps; 89 private final Context mContext; 90 private final HandlerThread mHandlerThread; 91 private final Handler mHandler; 92 private final NetworkProvider mProvider; 93 private final BlanketReservationOffer mBlanketOffer; 94 private final Set<ReservedServerOffer> mReservedServerOffers = new ArraySet<>(); 95 private final ClientOffer mClientOffer; 96 // mBluetoothManager guaranteed non-null when read on handler thread after start() is called 97 @Nullable 98 private BluetoothManager mBluetoothManager; 99 100 // Note: IFNAMSIZ is 16. 101 private static final String TUN_IFNAME = "l2cap-tun"; 102 private static int sTunIndex = 0; 103 104 /** 105 * The blanket reservation offer is used to create an L2CAP server network, i.e. a network 106 * based on a BluetoothServerSocket. 107 * 108 * Note that NetworkCapabilities matching semantics will cause onNetworkNeeded to be called for 109 * requests that do not have a NetworkSpecifier set. 110 */ 111 private class BlanketReservationOffer implements NetworkOfferCallback { 112 public static final NetworkScore SCORE = new NetworkScore.Builder().build(); 113 // Note the missing NET_CAPABILITY_NOT_RESTRICTED marking the network as restricted. 114 public static final NetworkCapabilities CAPABILITIES; 115 static { 116 // Below capabilities will match any reservation request with an L2capNetworkSpecifier 117 // that specifies ROLE_SERVER or without a NetworkSpecifier. 118 final L2capNetworkSpecifier l2capNetworkSpecifier = new L2capNetworkSpecifier.Builder() 119 .setRole(ROLE_SERVER) 120 .build(); 121 NetworkCapabilities caps = new NetworkCapabilities.Builder(COMMON_CAPABILITIES) 122 .setNetworkSpecifier(l2capNetworkSpecifier) 123 .build(); 124 // TODO: add #setReservationId() to NetworkCapabilities.Builder 125 caps.setReservationId(RES_ID_MATCH_ALL_RESERVATIONS); 126 CAPABILITIES = caps; 127 } 128 129 @Override onNetworkNeeded(NetworkRequest request)130 public void onNetworkNeeded(NetworkRequest request) { 131 // The NetworkSpecifier is guaranteed to be either null or an L2capNetworkSpecifier, so 132 // this cast is safe. 133 final L2capNetworkSpecifier specifier = 134 (L2capNetworkSpecifier) request.getNetworkSpecifier(); 135 if (specifier == null) return; 136 if (!specifier.isValidServerReservationSpecifier()) { 137 Log.i(TAG, "Ignoring invalid reservation request: " + request); 138 return; 139 } 140 141 final ReservedServerOffer reservedOffer = createReservedServerOffer(request); 142 if (reservedOffer == null) { 143 // Something went wrong when creating the offer. Send onUnavailable() to the app. 144 Log.e(TAG, "Failed to create L2cap server offer"); 145 mProvider.declareNetworkRequestUnfulfillable(request); 146 return; 147 } 148 149 final NetworkCapabilities reservedCaps = reservedOffer.getReservedCapabilities(); 150 mProvider.registerNetworkOffer(SCORE, reservedCaps, mHandler::post, reservedOffer); 151 mReservedServerOffers.add(reservedOffer); 152 } 153 154 @Nullable createReservedServerOffer(NetworkRequest reservation)155 private ReservedServerOffer createReservedServerOffer(NetworkRequest reservation) { 156 final BluetoothAdapter bluetoothAdapter = mBluetoothManager.getAdapter(); 157 if (bluetoothAdapter == null) { 158 Log.w(TAG, "Failed to get BluetoothAdapter"); 159 return null; 160 } 161 final BluetoothServerSocket serverSocket; 162 try { 163 serverSocket = bluetoothAdapter.listenUsingInsecureL2capChannel(); 164 } catch (IOException e) { 165 Log.w(TAG, "Failed to open BluetoothServerSocket"); 166 return null; 167 } 168 169 // Create the reserved capabilities partially from the reservation itself (non-reserved 170 // parts of the L2capNetworkSpecifier), the COMMON_CAPABILITIES, and the reserved data 171 // (BLE L2CAP PSM from the BluetoothServerSocket). 172 final NetworkCapabilities reservationNc = reservation.networkCapabilities; 173 final L2capNetworkSpecifier reservationSpec = 174 (L2capNetworkSpecifier) reservationNc.getNetworkSpecifier(); 175 // Note: the RemoteAddress is unspecified for server networks. 176 final L2capNetworkSpecifier reservedSpec = new L2capNetworkSpecifier.Builder() 177 .setRole(ROLE_SERVER) 178 .setHeaderCompression(reservationSpec.getHeaderCompression()) 179 .setPsm(serverSocket.getPsm()) 180 .build(); 181 NetworkCapabilities reservedNc = 182 new NetworkCapabilities.Builder(COMMON_CAPABILITIES) 183 .setNetworkSpecifier(reservedSpec) 184 .build(); 185 reservedNc.setReservationId(reservationNc.getReservationId()); 186 return new ReservedServerOffer(reservedNc, serverSocket); 187 } 188 189 @Nullable getReservedOfferForRequest(NetworkRequest request)190 private ReservedServerOffer getReservedOfferForRequest(NetworkRequest request) { 191 final int rId = request.networkCapabilities.getReservationId(); 192 for (ReservedServerOffer offer : mReservedServerOffers) { 193 // Comparing by reservationId is more explicit then using canBeSatisfiedBy() or the 194 // requestId. 195 if (offer.getReservedCapabilities().getReservationId() != rId) continue; 196 return offer; 197 } 198 return null; 199 } 200 201 @Override onNetworkUnneeded(NetworkRequest request)202 public void onNetworkUnneeded(NetworkRequest request) { 203 final ReservedServerOffer reservedOffer = getReservedOfferForRequest(request); 204 if (reservedOffer == null) return; 205 206 // Note that the reserved offer gets torn down when the reservation goes away, even if 207 // there are active (non-reservation) requests for said offer. 208 destroyAndUnregisterReservedOffer(reservedOffer); 209 } 210 } 211 destroyAndUnregisterReservedOffer(ReservedServerOffer reservedOffer)212 private void destroyAndUnregisterReservedOffer(ReservedServerOffer reservedOffer) { 213 HandlerUtils.ensureRunningOnHandlerThread(mHandler); 214 // Ensure the offer still exists if this was posted on the handler. 215 if (!mReservedServerOffers.contains(reservedOffer)) return; 216 mReservedServerOffers.remove(reservedOffer); 217 218 reservedOffer.tearDown(); 219 mProvider.unregisterNetworkOffer(reservedOffer); 220 } 221 222 @Nullable createL2capNetwork(BluetoothSocket socket, NetworkCapabilities caps, L2capNetwork.ICallback cb)223 private L2capNetwork createL2capNetwork(BluetoothSocket socket, NetworkCapabilities caps, 224 L2capNetwork.ICallback cb) { 225 HandlerUtils.ensureRunningOnHandlerThread(mHandler); 226 final String ifname = TUN_IFNAME + String.valueOf(sTunIndex++); 227 final ParcelFileDescriptor tunFd = mDeps.createTunInterface(ifname); 228 if (tunFd == null) { 229 return null; 230 } 231 232 return L2capNetwork.create( 233 mHandler, mContext, mProvider, ifname, socket, tunFd, caps, mDeps, cb); 234 } 235 closeBluetoothSocket(BluetoothSocket socket)236 private static void closeBluetoothSocket(BluetoothSocket socket) { 237 try { 238 socket.close(); 239 } catch (IOException e) { 240 Log.w(TAG, "Failed to close BluetoothSocket", e); 241 } 242 } 243 244 private class ReservedServerOffer implements NetworkOfferCallback { 245 private final NetworkCapabilities mReservedCapabilities; 246 private final AcceptThread mAcceptThread; 247 // This set should almost always contain at most one network. This is because all L2CAP 248 // server networks created by the same reserved offer are indistinguishable from each other, 249 // so that ConnectivityService will tear down all but the first. However, temporarily, there 250 // can be more than one network. 251 private final Set<L2capNetwork> mL2capNetworks = new ArraySet<>(); 252 253 private class AcceptThread extends Thread { 254 private static final int TIMEOUT_MS = 500; 255 private final BluetoothServerSocket mServerSocket; 256 AcceptThread(BluetoothServerSocket serverSocket)257 public AcceptThread(BluetoothServerSocket serverSocket) { 258 super("L2capNetworkProvider-AcceptThread"); 259 mServerSocket = serverSocket; 260 } 261 postDestroyAndUnregisterReservedOffer()262 private void postDestroyAndUnregisterReservedOffer() { 263 // Called on AcceptThread 264 mHandler.post(() -> { 265 destroyAndUnregisterReservedOffer(ReservedServerOffer.this); 266 }); 267 } 268 postCreateServerNetwork(BluetoothSocket connectedSocket)269 private void postCreateServerNetwork(BluetoothSocket connectedSocket) { 270 // Called on AcceptThread 271 mHandler.post(() -> { 272 final boolean success = createServerNetwork(connectedSocket); 273 if (!success) closeBluetoothSocket(connectedSocket); 274 }); 275 } 276 277 @Override run()278 public void run() { 279 while (true) { 280 final BluetoothSocket connectedSocket; 281 try { 282 connectedSocket = mServerSocket.accept(); 283 } catch (IOException e) { 284 // Note calling BluetoothServerSocket#close() also triggers an IOException 285 // which is indistinguishable from any other exceptional behavior. 286 // postDestroyAndUnregisterReservedOffer() is always safe to call as it 287 // first checks whether the offer still exists; so if the 288 // BluetoothServerSocket was closed (i.e. on tearDown()) this is a noop. 289 Log.w(TAG, "BluetoothServerSocket closed or #accept failed", e); 290 postDestroyAndUnregisterReservedOffer(); 291 return; // stop running immediately on error 292 } 293 postCreateServerNetwork(connectedSocket); 294 } 295 } 296 tearDown()297 public void tearDown() { 298 HandlerUtils.ensureRunningOnHandlerThread(mHandler); 299 try { 300 // BluetoothServerSocket.close() is thread-safe. 301 mServerSocket.close(); 302 } catch (IOException e) { 303 Log.w(TAG, "Failed to close BluetoothServerSocket", e); 304 } 305 try { 306 join(); 307 } catch (InterruptedException e) { 308 // join() interrupted during tearDown(). Do nothing. 309 } 310 } 311 } 312 createServerNetwork(BluetoothSocket socket)313 private boolean createServerNetwork(BluetoothSocket socket) { 314 HandlerUtils.ensureRunningOnHandlerThread(mHandler); 315 // It is possible the offer went away. 316 if (!mReservedServerOffers.contains(this)) return false; 317 318 if (!socket.isConnected()) { 319 Log.wtf(TAG, "BluetoothSocket must be connected"); 320 return false; 321 } 322 323 final L2capNetwork network = createL2capNetwork(socket, mReservedCapabilities, 324 new L2capNetwork.ICallback() { 325 @Override 326 public void onError(L2capNetwork network) { 327 HandlerUtils.ensureRunningOnHandlerThread(mHandler); 328 destroyAndUnregisterReservedOffer(ReservedServerOffer.this); 329 } 330 @Override 331 public void onNetworkUnwanted(L2capNetwork network) { 332 HandlerUtils.ensureRunningOnHandlerThread(mHandler); 333 // Leave reservation in place. 334 final boolean networkExists = mL2capNetworks.remove(network); 335 if (!networkExists) return; // already torn down. 336 network.tearDown(); 337 } 338 }); 339 340 if (network == null) { 341 Log.e(TAG, "Failed to create L2capNetwork"); 342 return false; 343 } 344 345 mL2capNetworks.add(network); 346 return true; 347 } 348 ReservedServerOffer(NetworkCapabilities reservedCapabilities, BluetoothServerSocket serverSocket)349 public ReservedServerOffer(NetworkCapabilities reservedCapabilities, 350 BluetoothServerSocket serverSocket) { 351 mReservedCapabilities = reservedCapabilities; 352 mAcceptThread = new AcceptThread(serverSocket); 353 mAcceptThread.start(); 354 } 355 getReservedCapabilities()356 public NetworkCapabilities getReservedCapabilities() { 357 return mReservedCapabilities; 358 } 359 360 @Override onNetworkNeeded(NetworkRequest request)361 public void onNetworkNeeded(NetworkRequest request) { 362 // UNUSED: the lifetime of the reserved network is controlled by the blanket offer. 363 } 364 365 @Override onNetworkUnneeded(NetworkRequest request)366 public void onNetworkUnneeded(NetworkRequest request) { 367 // UNUSED: the lifetime of the reserved network is controlled by the blanket offer. 368 } 369 370 /** Called when the reservation goes away and the reserved offer must be torn down. */ tearDown()371 public void tearDown() { 372 HandlerUtils.ensureRunningOnHandlerThread(mHandler); 373 mAcceptThread.tearDown(); 374 for (L2capNetwork network : mL2capNetworks) { 375 network.tearDown(); 376 } 377 } 378 } 379 380 private class ClientOffer implements NetworkOfferCallback { 381 public static final NetworkScore SCORE = new NetworkScore.Builder().build(); 382 public static final NetworkCapabilities CAPABILITIES; 383 static { 384 // Below capabilities will match any request with an L2capNetworkSpecifier 385 // that specifies ROLE_CLIENT or without a NetworkSpecifier. 386 final L2capNetworkSpecifier l2capNetworkSpecifier = new L2capNetworkSpecifier.Builder() 387 .setRole(ROLE_CLIENT) 388 .build(); 389 CAPABILITIES = new NetworkCapabilities.Builder(COMMON_CAPABILITIES) 390 .setNetworkSpecifier(l2capNetworkSpecifier) 391 .build(); 392 } 393 394 private final Map<L2capNetworkSpecifier, ClientRequestInfo> mClientNetworkRequests = 395 new ArrayMap<>(); 396 397 /** 398 * State object to store information for client NetworkRequests. 399 */ 400 private static class ClientRequestInfo { 401 public final L2capNetworkSpecifier specifier; 402 public final List<NetworkRequest> requests = new ArrayList<>(); 403 // TODO: add support for retries. 404 public final ConnectThread connectThread; 405 @Nullable 406 public L2capNetwork network; 407 ClientRequestInfo(NetworkRequest request, ConnectThread connectThread)408 public ClientRequestInfo(NetworkRequest request, ConnectThread connectThread) { 409 this.specifier = (L2capNetworkSpecifier) request.getNetworkSpecifier(); 410 this.requests.add(request); 411 this.connectThread = connectThread; 412 } 413 } 414 415 // TODO: consider using ExecutorService 416 private class ConnectThread extends Thread { 417 private final L2capNetworkSpecifier mSpecifier; 418 private final BluetoothSocket mSocket; 419 ConnectThread(L2capNetworkSpecifier specifier, BluetoothSocket socket)420 public ConnectThread(L2capNetworkSpecifier specifier, BluetoothSocket socket) { 421 super("L2capNetworkProvider-ConnectThread"); 422 mSpecifier = specifier; 423 mSocket = socket; 424 } 425 426 @Override run()427 public void run() { 428 try { 429 mSocket.connect(); 430 mHandler.post(() -> { 431 final boolean success = createClientNetwork(mSpecifier, mSocket); 432 if (!success) closeBluetoothSocket(mSocket); 433 }); 434 } catch (IOException e) { 435 Log.w(TAG, "BluetoothSocket was closed or #connect failed", e); 436 // It is safe to call BluetoothSocket#close() multiple times. 437 closeBluetoothSocket(mSocket); 438 mHandler.post(() -> { 439 // Note that if the Socket was closed, this call is a noop as the 440 // ClientNetworkRequest has already been removed. 441 declareAllNetworkRequestsUnfulfillable(mSpecifier); 442 }); 443 } 444 } 445 abort()446 public void abort() { 447 HandlerUtils.ensureRunningOnHandlerThread(mHandler); 448 // Closing the BluetoothSocket is the only way to unblock connect() because it calls 449 // shutdown on the underlying (connected) SOCK_SEQPACKET. 450 // It is safe to call BluetoothSocket#close() multiple times. 451 closeBluetoothSocket(mSocket); 452 try { 453 join(); 454 } catch (InterruptedException e) { 455 Log.i(TAG, "Interrupted while joining ConnectThread", e); 456 } 457 } 458 } 459 createClientNetwork(L2capNetworkSpecifier specifier, BluetoothSocket socket)460 private boolean createClientNetwork(L2capNetworkSpecifier specifier, 461 BluetoothSocket socket) { 462 HandlerUtils.ensureRunningOnHandlerThread(mHandler); 463 // Check whether request still exists 464 final ClientRequestInfo cri = mClientNetworkRequests.get(specifier); 465 if (cri == null) return false; 466 467 final NetworkCapabilities caps = new NetworkCapabilities.Builder(CAPABILITIES) 468 .setNetworkSpecifier(specifier) 469 .build(); 470 471 final L2capNetwork network = createL2capNetwork(socket, caps, 472 new L2capNetwork.ICallback() { 473 // TODO: do not send onUnavailable() after the network has become available. The 474 // right thing to do here is to tearDown the network (if it still exists, 475 // because note that the request might have already been removed in the 476 // meantime, so `network` cannot be used directly. 477 @Override 478 public void onError(L2capNetwork network) { 479 HandlerUtils.ensureRunningOnHandlerThread(mHandler); 480 declareAllNetworkRequestsUnfulfillable(specifier); 481 } 482 @Override 483 public void onNetworkUnwanted(L2capNetwork network) { 484 HandlerUtils.ensureRunningOnHandlerThread(mHandler); 485 declareAllNetworkRequestsUnfulfillable(specifier); 486 } 487 }); 488 if (network == null) return false; 489 490 cri.network = network; 491 return true; 492 } 493 494 @Override onNetworkNeeded(NetworkRequest request)495 public void onNetworkNeeded(NetworkRequest request) { 496 // The NetworkSpecifier is guaranteed to be either null or an L2capNetworkSpecifier, so 497 // this cast is safe. 498 final L2capNetworkSpecifier requestSpecifier = 499 (L2capNetworkSpecifier) request.getNetworkSpecifier(); 500 if (requestSpecifier == null) return; 501 if (!requestSpecifier.isValidClientRequestSpecifier()) { 502 Log.i(TAG, "Ignoring invalid client request: " + request); 503 return; 504 } 505 506 // Check whether this exact request is already being tracked. 507 final ClientRequestInfo cri = mClientNetworkRequests.get(requestSpecifier); 508 if (cri != null) { 509 Log.d(TAG, "The request is already being tracked. NetworkRequest: " + request); 510 cri.requests.add(request); 511 return; 512 } 513 514 // Check whether a fuzzy match shows a mismatch in header compression by calling 515 // canBeSatisfiedBy(). 516 // TODO: Add a copy constructor to L2capNetworkSpecifier.Builder. 517 final L2capNetworkSpecifier matchAnyHeaderCompressionSpecifier = 518 new L2capNetworkSpecifier.Builder() 519 .setRole(requestSpecifier.getRole()) 520 .setRemoteAddress(requestSpecifier.getRemoteAddress()) 521 .setPsm(requestSpecifier.getPsm()) 522 .setHeaderCompression(HEADER_COMPRESSION_ANY) 523 .build(); 524 for (L2capNetworkSpecifier existingSpecifier : mClientNetworkRequests.keySet()) { 525 if (existingSpecifier.canBeSatisfiedBy(matchAnyHeaderCompressionSpecifier)) { 526 // This requeset can never be serviced as this network already exists with a 527 // different header compression mechanism. 528 mProvider.declareNetworkRequestUnfulfillable(request); 529 return; 530 } 531 } 532 533 // If the code reaches here, this is a new request. 534 final BluetoothAdapter bluetoothAdapter = mBluetoothManager.getAdapter(); 535 if (bluetoothAdapter == null) { 536 Log.w(TAG, "Failed to get BluetoothAdapter"); 537 mProvider.declareNetworkRequestUnfulfillable(request); 538 return; 539 } 540 541 final byte[] macAddress = requestSpecifier.getRemoteAddress().toByteArray(); 542 final BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(macAddress); 543 final BluetoothSocket socket; 544 try { 545 socket = bluetoothDevice.createInsecureL2capChannel(requestSpecifier.getPsm()); 546 } catch (IOException e) { 547 Log.w(TAG, "Failed to createInsecureL2capChannel", e); 548 mProvider.declareNetworkRequestUnfulfillable(request); 549 return; 550 } 551 552 final ConnectThread connectThread = new ConnectThread(requestSpecifier, socket); 553 connectThread.start(); 554 final ClientRequestInfo newRequestInfo = new ClientRequestInfo(request, connectThread); 555 mClientNetworkRequests.put(requestSpecifier, newRequestInfo); 556 } 557 558 @Override onNetworkUnneeded(NetworkRequest request)559 public void onNetworkUnneeded(NetworkRequest request) { 560 final L2capNetworkSpecifier specifier = 561 (L2capNetworkSpecifier) request.getNetworkSpecifier(); 562 563 // Map#get() is safe to call with null key 564 final ClientRequestInfo cri = mClientNetworkRequests.get(specifier); 565 if (cri == null) return; 566 567 cri.requests.remove(request); 568 if (cri.requests.size() > 0) return; 569 570 // If the code reaches here, the network needs to be torn down. 571 releaseClientNetworkRequest(cri); 572 } 573 574 /** 575 * Release the client network request and tear down all associated state. 576 * 577 * Only call this when all associated NetworkRequests have been released. 578 */ releaseClientNetworkRequest(ClientRequestInfo cri)579 private void releaseClientNetworkRequest(ClientRequestInfo cri) { 580 HandlerUtils.ensureRunningOnHandlerThread(mHandler); 581 mClientNetworkRequests.remove(cri.specifier); 582 if (cri.connectThread.isAlive()) { 583 // Note that if ConnectThread succeeds between calling #isAlive() and #abort(), the 584 // request will already be removed from mClientNetworkRequests by the time the 585 // createClientNetwork() call is processed on the handler, so it is safe to call 586 // #abort(). 587 cri.connectThread.abort(); 588 } 589 590 if (cri.network != null) { 591 cri.network.tearDown(); 592 } 593 } 594 declareAllNetworkRequestsUnfulfillable(L2capNetworkSpecifier specifier)595 private void declareAllNetworkRequestsUnfulfillable(L2capNetworkSpecifier specifier) { 596 HandlerUtils.ensureRunningOnHandlerThread(mHandler); 597 final ClientRequestInfo cri = mClientNetworkRequests.get(specifier); 598 if (cri == null) return; 599 600 // Release ClientNetworkRequest before sending onUnavailable() to ensure the app 601 // first receives an onLost() callback if a network had been created. 602 releaseClientNetworkRequest(cri); 603 for (NetworkRequest request : cri.requests) { 604 mProvider.declareNetworkRequestUnfulfillable(request); 605 } 606 } 607 } 608 609 @VisibleForTesting 610 public static class Dependencies { 611 /** Get the HandlerThread for L2capNetworkProvider to run on */ getHandlerThread()612 public HandlerThread getHandlerThread() { 613 final HandlerThread thread = new HandlerThread("L2capNetworkProviderThread"); 614 thread.start(); 615 return thread; 616 } 617 618 /** Create a tun interface configured for blocking i/o */ 619 @Nullable createTunInterface(String ifname)620 public ParcelFileDescriptor createTunInterface(String ifname) { 621 final ParcelFileDescriptor fd; 622 try { 623 fd = ParcelFileDescriptor.adoptFd(ServiceConnectivityJni.createTunTap( 624 true /*isTun*/, 625 true /*hasCarrier*/, 626 true /*setIffMulticast*/, 627 ifname)); 628 ServiceConnectivityJni.bringUpInterface(ifname); 629 // TODO: consider adding a parameter to createTunTap() (or the Builder that should 630 // be added) to configure i/o blocking. 631 final int flags = Os.fcntlInt(fd.getFileDescriptor(), F_GETFL, 0); 632 Os.fcntlInt(fd.getFileDescriptor(), F_SETFL, flags & ~O_NONBLOCK); 633 } catch (Exception e) { 634 // Note: createTunTap currently throws an IllegalStateException on failure. 635 // TODO: native functions should throw ErrnoException. 636 Log.e(TAG, "Failed to create tun interface", e); 637 return null; 638 } 639 return fd; 640 } 641 642 /** Create an L2capPacketForwarder and start forwarding */ createL2capPacketForwarder(Handler handler, ParcelFileDescriptor tunFd, BluetoothSocket socket, boolean compressHeaders, L2capPacketForwarder.ICallback cb)643 public L2capPacketForwarder createL2capPacketForwarder(Handler handler, 644 ParcelFileDescriptor tunFd, BluetoothSocket socket, boolean compressHeaders, 645 L2capPacketForwarder.ICallback cb) { 646 return new L2capPacketForwarder(handler, tunFd, socket, compressHeaders, cb); 647 } 648 649 /** Create an L2capIpClient */ createL2capIpClient(String logTag, Context context, String ifname)650 public L2capIpClient createL2capIpClient(String logTag, Context context, String ifname) { 651 return new L2capIpClient(logTag, context, ifname); 652 } 653 } 654 L2capNetworkProvider(Context context)655 public L2capNetworkProvider(Context context) { 656 this(new Dependencies(), context); 657 } 658 L2capNetworkProvider(Dependencies deps, Context context)659 public L2capNetworkProvider(Dependencies deps, Context context) { 660 mDeps = deps; 661 mContext = context; 662 mHandlerThread = mDeps.getHandlerThread(); 663 mHandler = new Handler(mHandlerThread.getLooper()); 664 mProvider = new NetworkProvider(context, mHandlerThread.getLooper(), TAG); 665 mBlanketOffer = new BlanketReservationOffer(); 666 mClientOffer = new ClientOffer(); 667 } 668 669 /** 670 * Start L2capNetworkProvider. 671 * 672 * Called on CS Handler thread. 673 */ start()674 public void start() { 675 mHandler.post(() -> { 676 final PackageManager pm = mContext.getPackageManager(); 677 if (!pm.hasSystemFeature(FEATURE_BLUETOOTH_LE)) { 678 return; 679 } 680 mBluetoothManager = mContext.getSystemService(BluetoothManager.class); 681 if (mBluetoothManager == null) { 682 // Can this ever happen? 683 Log.wtf(TAG, "BluetoothManager not found"); 684 return; 685 } 686 mContext.getSystemService(ConnectivityManager.class).registerNetworkProvider(mProvider); 687 mProvider.registerNetworkOffer(BlanketReservationOffer.SCORE, 688 BlanketReservationOffer.CAPABILITIES, mHandler::post, mBlanketOffer); 689 mProvider.registerNetworkOffer(ClientOffer.SCORE, 690 ClientOffer.CAPABILITIES, mHandler::post, mClientOffer); 691 }); 692 } 693 } 694