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