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