• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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