• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Binder;
32 import android.os.Looper;
33 import android.os.RemoteException;
34 import android.text.TextUtils;
35 import android.util.Log;
36 
37 import com.android.internal.annotations.GuardedBy;
38 
39 /**
40  * This class tracks the data connection associated with Bluetooth
41  * reverse tethering. PanService calls it when a reverse tethered
42  * connection needs to be activated or deactivated.
43  *
44  * @hide
45  */
46 public class BluetoothTetheringNetworkFactory extends NetworkFactory {
47     private static final String NETWORK_TYPE = "Bluetooth Tethering";
48     private static final String TAG = "BluetoothTetheringNetworkFactory";
49     private static final int NETWORK_SCORE = 69;
50 
51     private final NetworkCapabilities mNetworkCapabilities;
52     private final Context mContext;
53     private final PanService mPanService;
54 
55     // All accesses to these must be synchronized(this).
56     private IIpClient mIpClient;
57     @GuardedBy("this")
58     private int mIpClientStartIndex = 0;
59     private String mInterfaceName;
60     private NetworkAgent mNetworkAgent;
61 
BluetoothTetheringNetworkFactory(Context context, Looper looper, PanService panService)62     public BluetoothTetheringNetworkFactory(Context context, Looper looper, PanService panService) {
63         super(looper, context, NETWORK_TYPE, new NetworkCapabilities());
64 
65         mContext = context;
66         mPanService = panService;
67 
68         mNetworkCapabilities = 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 onNetworkUnwanted() {
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         final long token = Binder.clearCallingIdentity();
207         try {
208             for (BluetoothDevice device : mPanService.getConnectedDevices()) {
209                 mPanService.disconnect(device);
210             }
211         } finally {
212             Binder.restoreCallingIdentity(token);
213         }
214     }
215 
216     // Called by PanService when a network interface for Bluetooth reverse-tethering
217     // becomes available.  We register our NetworkFactory at this point.
startReverseTether(final String iface)218     public void startReverseTether(final String iface) {
219         if (iface == null || TextUtils.isEmpty(iface)) {
220             Log.e(TAG, "attempted to reverse tether with empty interface");
221             return;
222         }
223         synchronized (this) {
224             if (!TextUtils.isEmpty(mInterfaceName)) {
225                 Log.e(TAG, "attempted to reverse tether while already in process");
226                 return;
227             }
228             mInterfaceName = iface;
229             // Advertise ourselves to ConnectivityService.
230             register();
231             setScoreFilter(NETWORK_SCORE);
232         }
233     }
234 
235     // Called by PanService when a network interface for Bluetooth reverse-tethering
236     // goes away.  We stop advertising ourselves to ConnectivityService at this point.
stopReverseTether()237     public synchronized void stopReverseTether() {
238         if (TextUtils.isEmpty(mInterfaceName)) {
239             Log.e(TAG, "attempted to stop reverse tether with nothing tethered");
240             return;
241         }
242         onCancelRequest();
243         setScoreFilter(-1);
244         terminate();
245     }
246 
initNetworkCapabilities()247     private NetworkCapabilities initNetworkCapabilities() {
248         final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder()
249                 .addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH)
250                 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
251                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
252                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
253                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
254                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
255                 // Bluetooth v3 and v4 go up to 24 Mbps.
256                 // TODO: Adjust this to actual connection bandwidth.
257                 .setLinkUpstreamBandwidthKbps(24 * 1000)
258                 .setLinkDownstreamBandwidthKbps(24 * 1000);
259         return builder.build();
260     }
261 }
262