• 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.NetworkCapabilities;
25 import android.net.NetworkFactory;
26 import android.net.NetworkInfo;
27 import android.net.NetworkInfo.DetailedState;
28 import android.net.ip.IIpClient;
29 import android.net.ip.IpClientUtil;
30 import android.net.ip.IpClientUtil.WaitForProvisioningCallbacks;
31 import android.net.shared.ProvisioningConfiguration;
32 import android.os.Looper;
33 import android.os.RemoteException;
34 import android.text.TextUtils;
35 import android.util.Slog;
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 final NetworkInfo mNetworkInfo;
57     private IIpClient mIpClient;
58     @GuardedBy("this")
59     private int mIpClientStartIndex = 0;
60     private String mInterfaceName;
61     private NetworkAgent mNetworkAgent;
62 
BluetoothTetheringNetworkFactory(Context context, Looper looper, PanService panService)63     public BluetoothTetheringNetworkFactory(Context context, Looper looper, PanService panService) {
64         super(looper, context, NETWORK_TYPE, new NetworkCapabilities());
65 
66         mContext = context;
67         mPanService = panService;
68 
69         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORK_TYPE, "");
70         mNetworkCapabilities = new NetworkCapabilities();
71         initNetworkCapabilities();
72         setCapabilityFilter(mNetworkCapabilities);
73     }
74 
75     private class BtIpClientCallback extends WaitForProvisioningCallbacks {
76         private final int mCurrentStartIndex;
77 
BtIpClientCallback(int currentStartIndex)78         private BtIpClientCallback(int currentStartIndex) {
79             mCurrentStartIndex = currentStartIndex;
80         }
81 
82         @Override
onIpClientCreated(IIpClient ipClient)83         public void onIpClientCreated(IIpClient ipClient) {
84             synchronized (BluetoothTetheringNetworkFactory.this) {
85                 if (mCurrentStartIndex != mIpClientStartIndex) {
86                     // Do not start IpClient: the current request is obsolete.
87                     // IpClient will be GCed eventually as the IIpClient Binder token goes out
88                     // of scope.
89                     return;
90                 }
91                 mIpClient = ipClient;
92                 try {
93                     mIpClient.startProvisioning(new ProvisioningConfiguration.Builder()
94                             .withoutMultinetworkPolicyTracker()
95                             .withoutIpReachabilityMonitor()
96                             .build().toStableParcelable());
97                 } catch (RemoteException e) {
98                     Slog.e(TAG, "Error starting IpClient provisioning", e);
99                 }
100             }
101         }
102 
103         @Override
onLinkPropertiesChange(LinkProperties newLp)104         public void onLinkPropertiesChange(LinkProperties newLp) {
105             synchronized (BluetoothTetheringNetworkFactory.this) {
106                 if (mNetworkAgent != null && mNetworkInfo.isConnected()) {
107                     mNetworkAgent.sendLinkProperties(newLp);
108                 }
109             }
110         }
111     }
112 
stopIpClientLocked()113     private void stopIpClientLocked() {
114         // Mark all previous start requests as obsolete
115         mIpClientStartIndex++;
116         if (mIpClient != null) {
117             try {
118                 mIpClient.shutdown();
119             } catch (RemoteException e) {
120                 Slog.e(TAG, "Error shutting down IpClient", e);
121             }
122             mIpClient = null;
123         }
124     }
125 
startIpClientLocked()126     private BtIpClientCallback startIpClientLocked() {
127         mIpClientStartIndex++;
128         final BtIpClientCallback callback = new BtIpClientCallback(mIpClientStartIndex);
129         IpClientUtil.makeIpClient(mContext, mInterfaceName, callback);
130         return callback;
131     }
132 
133     // Called by NetworkFactory when PanService and NetworkFactory both desire a Bluetooth
134     // reverse-tether connection.  A network interface for Bluetooth reverse-tethering can be
135     // assumed to be available because we only register our NetworkFactory when it is so.
136     @Override
startNetwork()137     protected void startNetwork() {
138         // TODO: Figure out how to replace this thread with simple invocations
139         // of IpClient. This will likely necessitate a rethink about
140         // NetworkAgent, NetworkInfo, and associated instance lifetimes.
141         Thread ipProvisioningThread = new Thread(new Runnable() {
142             @Override
143             public void run() {
144                 LinkProperties linkProperties;
145                 final WaitForProvisioningCallbacks ipcCallback;
146 
147                 synchronized (BluetoothTetheringNetworkFactory.this) {
148                     if (TextUtils.isEmpty(mInterfaceName)) {
149                         Slog.e(TAG, "attempted to reverse tether without interface name");
150                         return;
151                     }
152                     log("ipProvisioningThread(+" + mInterfaceName + "): " + "mNetworkInfo="
153                             + mNetworkInfo);
154                     ipcCallback = startIpClientLocked();
155                     mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, null);
156                 }
157 
158                 linkProperties = ipcCallback.waitForProvisioning();
159                 if (linkProperties == null) {
160                     Slog.e(TAG, "IP provisioning error.");
161                     synchronized (BluetoothTetheringNetworkFactory.this) {
162                         stopIpClientLocked();
163                         setScoreFilter(-1);
164                     }
165                     return;
166                 }
167 
168                 synchronized (BluetoothTetheringNetworkFactory.this) {
169                     mNetworkInfo.setIsAvailable(true);
170                     mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
171 
172                     // Create our NetworkAgent.
173                     mNetworkAgent =
174                             new NetworkAgent(getLooper(), mContext, NETWORK_TYPE, mNetworkInfo,
175                                     mNetworkCapabilities, linkProperties, NETWORK_SCORE) {
176                                 @Override
177                                 public void unwanted() {
178                                     BluetoothTetheringNetworkFactory.this.onCancelRequest();
179                                 }
180 
181                                 ;
182                             };
183                 }
184             }
185         });
186         ipProvisioningThread.start();
187     }
188 
189     // Called from NetworkFactory to indicate ConnectivityService no longer desires a Bluetooth
190     // reverse-tether network.
191     @Override
stopNetwork()192     protected void stopNetwork() {
193         // Let NetworkAgent disconnect do the teardown.
194     }
195 
196     // Called by the NetworkFactory, NetworkAgent or PanService to tear down network.
onCancelRequest()197     private synchronized void onCancelRequest() {
198         stopIpClientLocked();
199         mInterfaceName = "";
200 
201         mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
202         if (mNetworkAgent != null) {
203             mNetworkAgent.sendNetworkInfo(mNetworkInfo);
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             Slog.e(TAG, "attempted to reverse tether with empty interface");
216             return;
217         }
218         synchronized (this) {
219             if (!TextUtils.isEmpty(mInterfaceName)) {
220                 Slog.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             Slog.e(TAG, "attempted to stop reverse tether with nothing tethered");
235             return;
236         }
237         onCancelRequest();
238         setScoreFilter(-1);
239         unregister();
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         // Bluetooth v3 and v4 go up to 24 Mbps.
248         // TODO: Adjust this to actual connection bandwidth.
249         mNetworkCapabilities.setLinkUpstreamBandwidthKbps(24 * 1000);
250         mNetworkCapabilities.setLinkDownstreamBandwidthKbps(24 * 1000);
251     }
252 }
253