1 /*
2  * Copyright (C) 2014 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.bluetooth.pan;
18 
19 import android.bluetooth.BluetoothDevice;
20 import android.content.Context;
21 import android.net.ConnectivityManager;
22 import android.net.LinkProperties;
23 import android.net.NetworkAgent;
24 import android.net.NetworkAgentConfig;
25 import android.net.NetworkCapabilities;
26 import android.net.NetworkFactory;
27 import android.net.ip.IIpClient;
28 import android.net.ip.IpClientUtil;
29 import android.net.ip.IpClientUtil.WaitForProvisioningCallbacks;
30 import android.net.shared.ProvisioningConfiguration;
31 import android.os.Looper;
32 import android.os.RemoteException;
33 import android.text.TextUtils;
34 import android.util.Log;
35 
36 import com.android.internal.annotations.GuardedBy;
37 
38 /**
39  * This class tracks the data connection associated with Bluetooth
40  * reverse tethering. PanService calls it when a reverse tethered
41  * connection needs to be activated or deactivated.
42  *
43  * @hide
44  */
45 public class BluetoothTetheringNetworkFactory extends NetworkFactory {
46     private static final String NETWORK_TYPE = "Bluetooth Tethering";
47     private static final String TAG = "BluetoothTetheringNetworkFactory";
48     private static final int NETWORK_SCORE = 69;
49 
50     private final NetworkCapabilities mNetworkCapabilities;
51     private final Context mContext;
52     private final PanService mPanService;
53 
54     // All accesses to these must be synchronized(this).
55     private IIpClient mIpClient;
56     @GuardedBy("this")
57     private int mIpClientStartIndex = 0;
58     private String mInterfaceName;
59     private NetworkAgent mNetworkAgent;
60 
BluetoothTetheringNetworkFactory(Context context, Looper looper, PanService panService)61     public BluetoothTetheringNetworkFactory(Context context, Looper looper, PanService panService) {
62         super(looper, context, NETWORK_TYPE, new NetworkCapabilities());
63 
64         mContext = context;
65         mPanService = panService;
66 
67         mNetworkCapabilities = new NetworkCapabilities();
68         initNetworkCapabilities();
69         setCapabilityFilter(mNetworkCapabilities);
70     }
71 
72     private class BtIpClientCallback extends WaitForProvisioningCallbacks {
73         private final int mCurrentStartIndex;
74 
BtIpClientCallback(int currentStartIndex)75         private BtIpClientCallback(int currentStartIndex) {
76             mCurrentStartIndex = currentStartIndex;
77         }
78 
79         @Override
onIpClientCreated(IIpClient ipClient)80         public void onIpClientCreated(IIpClient ipClient) {
81             synchronized (BluetoothTetheringNetworkFactory.this) {
82                 if (mCurrentStartIndex != mIpClientStartIndex) {
83                     // Do not start IpClient: the current request is obsolete.
84                     // IpClient will be GCed eventually as the IIpClient Binder token goes out
85                     // of scope.
86                     return;
87                 }
88                 mIpClient = ipClient;
89                 try {
90                     mIpClient.startProvisioning(new ProvisioningConfiguration.Builder()
91                             .withoutMultinetworkPolicyTracker()
92                             .withoutIpReachabilityMonitor()
93                             .build().toStableParcelable());
94                 } catch (RemoteException e) {
95                     Log.e(TAG, "Error starting IpClient provisioning", e);
96                 }
97             }
98         }
99 
100         @Override
onLinkPropertiesChange(LinkProperties newLp)101         public void onLinkPropertiesChange(LinkProperties newLp) {
102             synchronized (BluetoothTetheringNetworkFactory.this) {
103                 if (mNetworkAgent != null) {
104                     mNetworkAgent.sendLinkProperties(newLp);
105                 }
106             }
107         }
108     }
109 
stopIpClientLocked()110     private void stopIpClientLocked() {
111         // Mark all previous start requests as obsolete
112         mIpClientStartIndex++;
113         if (mIpClient != null) {
114             try {
115                 mIpClient.shutdown();
116             } catch (RemoteException e) {
117                 Log.e(TAG, "Error shutting down IpClient", e);
118             }
119             mIpClient = null;
120         }
121     }
122 
startIpClientLocked()123     private BtIpClientCallback startIpClientLocked() {
124         mIpClientStartIndex++;
125         final BtIpClientCallback callback = new BtIpClientCallback(mIpClientStartIndex);
126         IpClientUtil.makeIpClient(mContext, mInterfaceName, callback);
127         return callback;
128     }
129 
130     // Called by NetworkFactory when PanService and NetworkFactory both desire a Bluetooth
131     // reverse-tether connection.  A network interface for Bluetooth reverse-tethering can be
132     // assumed to be available because we only register our NetworkFactory when it is so.
133     @Override
startNetwork()134     protected void startNetwork() {
135         // TODO: Figure out how to replace this thread with simple invocations
136         // of IpClient. This will likely necessitate a rethink about
137         // NetworkAgent and associated instance lifetimes.
138         Thread ipProvisioningThread = new Thread(new Runnable() {
139             @Override
140             public void run() {
141                 final WaitForProvisioningCallbacks ipcCallback;
142                 final int ipClientStartIndex;
143 
144                 synchronized (BluetoothTetheringNetworkFactory.this) {
145                     if (TextUtils.isEmpty(mInterfaceName)) {
146                         Log.e(TAG, "attempted to reverse tether without interface name");
147                         return;
148                     }
149                     log("ipProvisioningThread(+" + mInterfaceName + ") start IP provisioning");
150                     ipcCallback = startIpClientLocked();
151                     ipClientStartIndex = mIpClientStartIndex;
152                 }
153 
154                 final LinkProperties linkProperties = ipcCallback.waitForProvisioning();
155                 if (linkProperties == null) {
156                     Log.e(TAG, "IP provisioning error.");
157                     synchronized (BluetoothTetheringNetworkFactory.this) {
158                         stopIpClientLocked();
159                         setScoreFilter(-1);
160                     }
161                     return;
162                 }
163                 final NetworkAgentConfig config = new NetworkAgentConfig.Builder()
164                         .setLegacyType(ConnectivityManager.TYPE_BLUETOOTH)
165                         .setLegacyTypeName(NETWORK_TYPE)
166                         .build();
167 
168                 synchronized (BluetoothTetheringNetworkFactory.this) {
169                     // Reverse tethering has been stopped, and stopping won the race : there is
170                     // no point in creating the agent (and it would be leaked), so bail.
171                     if (ipClientStartIndex != mIpClientStartIndex) return;
172                     // Create our NetworkAgent.
173                     mNetworkAgent =
174                             new NetworkAgent(mContext, getLooper(), NETWORK_TYPE,
175                                     mNetworkCapabilities, linkProperties, NETWORK_SCORE,
176                                     config, getProvider()) {
177                                 @Override
178                                 public void unwanted() {
179                                     BluetoothTetheringNetworkFactory.this.onCancelRequest();
180                                 }
181                             };
182                     mNetworkAgent.register();
183                     mNetworkAgent.markConnected();
184                 }
185             }
186         });
187         ipProvisioningThread.start();
188     }
189 
190     // Called from NetworkFactory to indicate ConnectivityService no longer desires a Bluetooth
191     // reverse-tether network.
192     @Override
stopNetwork()193     protected void stopNetwork() {
194         // Let NetworkAgent disconnect do the teardown.
195     }
196 
197     // Called by the NetworkFactory, NetworkAgent or PanService to tear down network.
onCancelRequest()198     private synchronized void onCancelRequest() {
199         stopIpClientLocked();
200         mInterfaceName = "";
201 
202         if (mNetworkAgent != null) {
203             mNetworkAgent.unregister();
204             mNetworkAgent = null;
205         }
206         for (BluetoothDevice device : mPanService.getConnectedDevices()) {
207             mPanService.disconnect(device);
208         }
209     }
210 
211     // Called by PanService when a network interface for Bluetooth reverse-tethering
212     // becomes available.  We register our NetworkFactory at this point.
startReverseTether(final String iface)213     public void startReverseTether(final String iface) {
214         if (iface == null || TextUtils.isEmpty(iface)) {
215             Log.e(TAG, "attempted to reverse tether with empty interface");
216             return;
217         }
218         synchronized (this) {
219             if (!TextUtils.isEmpty(mInterfaceName)) {
220                 Log.e(TAG, "attempted to reverse tether while already in process");
221                 return;
222             }
223             mInterfaceName = iface;
224             // Advertise ourselves to ConnectivityService.
225             register();
226             setScoreFilter(NETWORK_SCORE);
227         }
228     }
229 
230     // Called by PanService when a network interface for Bluetooth reverse-tethering
231     // goes away.  We stop advertising ourselves to ConnectivityService at this point.
stopReverseTether()232     public synchronized void stopReverseTether() {
233         if (TextUtils.isEmpty(mInterfaceName)) {
234             Log.e(TAG, "attempted to stop reverse tether with nothing tethered");
235             return;
236         }
237         onCancelRequest();
238         setScoreFilter(-1);
239         terminate();
240     }
241 
initNetworkCapabilities()242     private void initNetworkCapabilities() {
243         mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH);
244         mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
245         mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
246         mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
247         mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
248         // Bluetooth v3 and v4 go up to 24 Mbps.
249         // TODO: Adjust this to actual connection bandwidth.
250         mNetworkCapabilities.setLinkUpstreamBandwidthKbps(24 * 1000);
251         mNetworkCapabilities.setLinkDownstreamBandwidthKbps(24 * 1000);
252     }
253 }
254