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