• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 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.net;
18 
19 import static android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_6LOWPAN;
20 
21 import android.annotation.Nullable;
22 import android.bluetooth.BluetoothSocket;
23 import android.content.Context;
24 import android.net.L2capNetworkSpecifier;
25 import android.net.LinkProperties;
26 import android.net.NetworkAgent;
27 import android.net.NetworkAgentConfig;
28 import android.net.NetworkCapabilities;
29 import android.net.NetworkProvider;
30 import android.net.NetworkScore;
31 import android.net.ip.IIpClient;
32 import android.net.ip.IpClientCallbacks;
33 import android.net.ip.IpClientManager;
34 import android.net.ip.IpClientUtil;
35 import android.net.shared.ProvisioningConfiguration;
36 import android.os.ConditionVariable;
37 import android.os.Handler;
38 import android.os.ParcelFileDescriptor;
39 import android.util.Log;
40 
41 import com.android.server.L2capNetworkProvider;
42 
43 public class L2capNetwork {
44     private static final NetworkScore NETWORK_SCORE = new NetworkScore.Builder().build();
45     private final String mLogTag;
46     private final Handler mHandler;
47     private final L2capPacketForwarder mForwarder;
48     private final NetworkCapabilities mNetworkCapabilities;
49     private final NetworkAgent mNetworkAgent;
50 
51     /** IpClient wrapper to handle IPv6 link-local provisioning for L2CAP tun.
52      *
53      * Note that the IpClient does not need to be stopped.
54      */
55     public static class L2capIpClient extends IpClientCallbacks {
56         private final String mLogTag;
57         private final ConditionVariable mOnIpClientCreatedCv = new ConditionVariable(false);
58         private final ConditionVariable mOnProvisioningSuccessCv = new ConditionVariable(false);
59         @Nullable
60         private IpClientManager mIpClient;
61         @Nullable
62         private volatile LinkProperties mLinkProperties;
63 
L2capIpClient(String logTag, Context context, String ifname)64         public L2capIpClient(String logTag, Context context, String ifname) {
65             mLogTag = logTag;
66             IpClientUtil.makeIpClient(context, ifname, this);
67         }
68 
69         @Override
onIpClientCreated(IIpClient ipClient)70         public void onIpClientCreated(IIpClient ipClient) {
71             mIpClient = new IpClientManager(ipClient, mLogTag);
72             mOnIpClientCreatedCv.open();
73         }
74 
75         @Override
onProvisioningSuccess(LinkProperties lp)76         public void onProvisioningSuccess(LinkProperties lp) {
77             Log.d(mLogTag, "Successfully provisioned l2cap tun: " + lp);
78             mLinkProperties = lp;
79             mOnProvisioningSuccessCv.open();
80         }
81 
82         @Override
onProvisioningFailure(LinkProperties lp)83         public void onProvisioningFailure(LinkProperties lp) {
84             Log.i(mLogTag, "Failed to provision l2cap tun: " + lp);
85             mLinkProperties = null;
86             mOnProvisioningSuccessCv.open();
87         }
88 
89         /**
90          * Starts IPv6 link-local provisioning.
91          *
92          * @return LinkProperties on success, null on failure.
93          */
94         @Nullable
start()95         public LinkProperties start() {
96             mOnIpClientCreatedCv.block();
97             // mIpClient guaranteed non-null.
98             final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
99                     .withoutIPv4()
100                     .withIpv6LinkLocalOnly()
101                     .withRandomMacAddress() // addr_gen_mode EUI64 -> random on tun.
102                     .build();
103             mIpClient.startProvisioning(config);
104             // "Provisioning" is guaranteed to succeed as link-local only mode does not actually
105             // require any provisioning.
106             mOnProvisioningSuccessCv.block();
107             return mLinkProperties;
108         }
109     }
110 
111     public interface ICallback {
112         /** Called when an error is encountered */
onError(L2capNetwork network)113         void onError(L2capNetwork network);
114         /** Called when CS triggers NetworkAgent#onNetworkUnwanted */
onNetworkUnwanted(L2capNetwork network)115         void onNetworkUnwanted(L2capNetwork network);
116     }
117 
L2capNetwork(String logTag, Handler handler, Context context, NetworkProvider provider, BluetoothSocket socket, ParcelFileDescriptor tunFd, NetworkCapabilities nc, LinkProperties lp, L2capNetworkProvider.Dependencies deps, ICallback cb)118     public L2capNetwork(String logTag, Handler handler, Context context, NetworkProvider provider,
119             BluetoothSocket socket, ParcelFileDescriptor tunFd, NetworkCapabilities nc,
120             LinkProperties lp, L2capNetworkProvider.Dependencies deps, ICallback cb) {
121         mLogTag = logTag;
122         mHandler = handler;
123         mNetworkCapabilities = nc;
124 
125         final L2capNetworkSpecifier spec = (L2capNetworkSpecifier) nc.getNetworkSpecifier();
126         final boolean compressHeaders = spec.getHeaderCompression() == HEADER_COMPRESSION_6LOWPAN;
127 
128         mForwarder = deps.createL2capPacketForwarder(handler, tunFd, socket, compressHeaders,
129                 () -> {
130             // TODO: add a check that this callback is invoked on the handler thread.
131             cb.onError(L2capNetwork.this);
132         });
133 
134         final NetworkAgentConfig config = new NetworkAgentConfig.Builder().build();
135         mNetworkAgent = new NetworkAgent(context, mHandler.getLooper(), mLogTag,
136                 nc, lp, NETWORK_SCORE, config, provider) {
137             @Override
138             public void onNetworkUnwanted() {
139                 Log.i(mLogTag, "Network is unwanted");
140                 // TODO: add a check that this callback is invoked on the handler thread.
141                 cb.onNetworkUnwanted(L2capNetwork.this);
142             }
143         };
144         mNetworkAgent.register();
145         mNetworkAgent.markConnected();
146     }
147 
148     /** Create an L2capNetwork or return null on failure. */
149     @Nullable
create(Handler handler, Context context, NetworkProvider provider, String ifname, BluetoothSocket socket, ParcelFileDescriptor tunFd, NetworkCapabilities nc, L2capNetworkProvider.Dependencies deps, ICallback cb)150     public static L2capNetwork create(Handler handler, Context context, NetworkProvider provider,
151             String ifname, BluetoothSocket socket, ParcelFileDescriptor tunFd,
152             NetworkCapabilities nc, L2capNetworkProvider.Dependencies deps, ICallback cb) {
153         // TODO: add a check that this function is invoked on the handler thread.
154         final String logTag = String.format("L2capNetwork[%s]", ifname);
155 
156         // L2capIpClient#start() blocks until provisioning either succeeds (and returns
157         // LinkProperties) or fails (and returns null).
158         // Note that since L2capNetwork is using IPv6 link-local provisioning the most likely
159         // (only?) failure mode is due to the interface disappearing.
160         final LinkProperties lp = deps.createL2capIpClient(logTag, context, ifname).start();
161         if (lp == null) return null;
162 
163         return new L2capNetwork(
164                 logTag, handler, context, provider, socket, tunFd, nc, lp, deps, cb);
165     }
166 
167     /** Get the NetworkCapabilities used for this Network */
getNetworkCapabilities()168     public NetworkCapabilities getNetworkCapabilities() {
169         return mNetworkCapabilities;
170     }
171 
172     /** Tear down the network and associated resources */
tearDown()173     public void tearDown() {
174         mNetworkAgent.unregister();
175         mForwarder.tearDown();
176     }
177 }
178