• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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 android.server;
18 
19 import android.bluetooth.BluetoothAdapter;
20 import android.bluetooth.BluetoothDevice;
21 import android.bluetooth.BluetoothPan;
22 import android.bluetooth.BluetoothProfile;
23 import android.bluetooth.BluetoothTetheringDataTracker;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.res.Resources.NotFoundException;
29 import android.net.ConnectivityManager;
30 import android.net.InterfaceConfiguration;
31 import android.net.LinkAddress;
32 import android.net.NetworkUtils;
33 import android.os.IBinder;
34 import android.os.INetworkManagementService;
35 import android.os.ServiceManager;
36 import android.util.Log;
37 
38 import java.net.InetAddress;
39 import java.util.ArrayList;
40 import java.util.HashMap;
41 import java.util.List;
42 
43 /**
44  * This handles the PAN profile. All calls into this are made
45  * from Bluetooth Service.
46  */
47 final class BluetoothPanProfileHandler {
48     private static final String TAG = "BluetoothPanProfileHandler";
49     private static final boolean DBG = true;
50 
51     private ArrayList<String> mBluetoothIfaceAddresses;
52     private int mMaxPanDevices;
53 
54     private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1";
55     private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5;
56     private static final int BLUETOOTH_PREFIX_LENGTH        = 24;
57     public static BluetoothPanProfileHandler sInstance;
58     private final HashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices;
59     private boolean mTetheringOn;
60     private Context mContext;
61     private BluetoothService mBluetoothService;
62 
63     static final String NAP_ROLE = "nap";
64     static final String NAP_BRIDGE = "pan1";
65 
BluetoothPanProfileHandler(Context context, BluetoothService service)66     private BluetoothPanProfileHandler(Context context, BluetoothService service) {
67         mContext = context;
68         mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>();
69         mBluetoothService = service;
70         mTetheringOn = false;
71         mBluetoothIfaceAddresses = new ArrayList<String>();
72         try {
73             mMaxPanDevices = context.getResources().getInteger(
74                             com.android.internal.R.integer.config_max_pan_devices);
75         } catch (NotFoundException e) {
76             mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS;
77         }
78     }
79 
getInstance(Context context, BluetoothService service)80     static BluetoothPanProfileHandler getInstance(Context context,
81             BluetoothService service) {
82         if (sInstance == null) sInstance = new BluetoothPanProfileHandler(context, service);
83         return sInstance;
84     }
85 
isTetheringOn()86     boolean isTetheringOn() {
87         return mTetheringOn;
88     }
89 
allowIncomingTethering()90     boolean allowIncomingTethering() {
91         if (isTetheringOn() && getConnectedPanDevices().size() < mMaxPanDevices)
92             return true;
93         return false;
94     }
95 
96     private BroadcastReceiver mTetheringReceiver = null;
97 
setBluetoothTethering(boolean value)98     void setBluetoothTethering(boolean value) {
99         if (!value) {
100             disconnectPanServerDevices();
101         }
102 
103         if (mBluetoothService.getBluetoothState() != BluetoothAdapter.STATE_ON && value) {
104             IntentFilter filter = new IntentFilter();
105             filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
106             mTetheringReceiver = new BroadcastReceiver() {
107                 @Override
108                 public void onReceive(Context context, Intent intent) {
109                     if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
110                             == BluetoothAdapter.STATE_ON) {
111                         mTetheringOn = true;
112                         mContext.unregisterReceiver(mTetheringReceiver);
113                     }
114                 }
115             };
116             mContext.registerReceiver(mTetheringReceiver, filter);
117         } else {
118             mTetheringOn = value;
119         }
120     }
121 
getPanDeviceConnectionState(BluetoothDevice device)122     int getPanDeviceConnectionState(BluetoothDevice device) {
123         BluetoothPanDevice panDevice = mPanDevices.get(device);
124         if (panDevice == null) {
125             return BluetoothPan.STATE_DISCONNECTED;
126         }
127         return panDevice.mState;
128     }
129 
connectPanDevice(BluetoothDevice device)130     boolean connectPanDevice(BluetoothDevice device) {
131         String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
132         if (DBG) Log.d(TAG, "connect PAN(" + objectPath + ")");
133         if (getPanDeviceConnectionState(device) != BluetoothPan.STATE_DISCONNECTED) {
134             errorLog(device + " already connected to PAN");
135         }
136 
137         int connectedCount = 0;
138         for (BluetoothDevice panDevice: mPanDevices.keySet()) {
139             if (getPanDeviceConnectionState(panDevice) == BluetoothPan.STATE_CONNECTED) {
140                 connectedCount ++;
141             }
142         }
143         if (connectedCount > 8) {
144             debugLog(device + " could not connect to PAN because 8 other devices are"
145                     + "already connected");
146             return false;
147         }
148 
149         // Send interface as null as it is not known
150         handlePanDeviceStateChange(device, null, BluetoothPan.STATE_CONNECTING,
151                                            BluetoothPan.LOCAL_PANU_ROLE);
152         if (mBluetoothService.connectPanDeviceNative(objectPath, "nap")) {
153             debugLog("connecting to PAN");
154             return true;
155         } else {
156             handlePanDeviceStateChange(device, null, BluetoothPan.STATE_DISCONNECTED,
157                                                 BluetoothPan.LOCAL_PANU_ROLE);
158             errorLog("could not connect to PAN");
159             return false;
160         }
161     }
162 
disconnectPanServerDevices()163     private boolean disconnectPanServerDevices() {
164         debugLog("disconnect all PAN devices");
165 
166         for (BluetoothDevice device: mPanDevices.keySet()) {
167             BluetoothPanDevice panDevice = mPanDevices.get(device);
168             int state = panDevice.mState;
169             if (state == BluetoothPan.STATE_CONNECTED &&
170                     panDevice.mLocalRole == BluetoothPan.LOCAL_NAP_ROLE) {
171                 String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
172 
173                 handlePanDeviceStateChange(device, panDevice.mIface,
174                         BluetoothPan.STATE_DISCONNECTING, panDevice.mLocalRole);
175 
176                 if (!mBluetoothService.disconnectPanServerDeviceNative(objectPath,
177                         device.getAddress(),
178                         panDevice.mIface)) {
179                     errorLog("could not disconnect Pan Server Device "+device.getAddress());
180 
181                     // Restore prev state
182                     handlePanDeviceStateChange(device, panDevice.mIface, state,
183                             panDevice.mLocalRole);
184 
185                     return false;
186                 }
187             }
188         }
189         return true;
190     }
191 
getConnectedPanDevices()192     List<BluetoothDevice> getConnectedPanDevices() {
193         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
194 
195         for (BluetoothDevice device: mPanDevices.keySet()) {
196             if (getPanDeviceConnectionState(device) == BluetoothPan.STATE_CONNECTED) {
197                 devices.add(device);
198             }
199         }
200         return devices;
201     }
202 
getPanDevicesMatchingConnectionStates(int[] states)203     List<BluetoothDevice> getPanDevicesMatchingConnectionStates(int[] states) {
204         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
205 
206         for (BluetoothDevice device: mPanDevices.keySet()) {
207             int panDeviceState = getPanDeviceConnectionState(device);
208             for (int state : states) {
209                 if (state == panDeviceState) {
210                     devices.add(device);
211                     break;
212                 }
213             }
214         }
215         return devices;
216     }
217 
disconnectPanDevice(BluetoothDevice device)218     boolean disconnectPanDevice(BluetoothDevice device) {
219         String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
220         debugLog("disconnect PAN(" + objectPath + ")");
221 
222         int state = getPanDeviceConnectionState(device);
223         if (state != BluetoothPan.STATE_CONNECTED) {
224             debugLog(device + " already disconnected from PAN");
225             return false;
226         }
227 
228         BluetoothPanDevice panDevice = mPanDevices.get(device);
229 
230         if (panDevice == null) {
231             errorLog("No record for this Pan device:" + device);
232             return false;
233         }
234 
235         handlePanDeviceStateChange(device, panDevice.mIface, BluetoothPan.STATE_DISCONNECTING,
236                                     panDevice.mLocalRole);
237         if (panDevice.mLocalRole == BluetoothPan.LOCAL_NAP_ROLE) {
238             if (!mBluetoothService.disconnectPanServerDeviceNative(objectPath, device.getAddress(),
239                     panDevice.mIface)) {
240                 // Restore prev state, this shouldn't happen
241                 handlePanDeviceStateChange(device, panDevice.mIface, state, panDevice.mLocalRole);
242                 return false;
243             }
244         } else {
245             if (!mBluetoothService.disconnectPanDeviceNative(objectPath)) {
246                 // Restore prev state, this shouldn't happen
247                 handlePanDeviceStateChange(device, panDevice.mIface, state, panDevice.mLocalRole);
248                 return false;
249             }
250         }
251         return true;
252     }
253 
handlePanDeviceStateChange(BluetoothDevice device, String iface, int state, int role)254     void handlePanDeviceStateChange(BluetoothDevice device,
255                                                  String iface, int state, int role) {
256         int prevState;
257         String ifaceAddr = null;
258         BluetoothPanDevice panDevice = mPanDevices.get(device);
259 
260         if (panDevice == null) {
261             prevState = BluetoothPan.STATE_DISCONNECTED;
262         } else {
263             prevState = panDevice.mState;
264             ifaceAddr = panDevice.mIfaceAddr;
265         }
266         if (prevState == state) return;
267 
268         if (role == BluetoothPan.LOCAL_NAP_ROLE) {
269             if (state == BluetoothPan.STATE_CONNECTED) {
270                 ifaceAddr = enableTethering(iface);
271                 if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface");
272             } else if (state == BluetoothPan.STATE_DISCONNECTED) {
273                 if (ifaceAddr != null) {
274                     mBluetoothIfaceAddresses.remove(ifaceAddr);
275                     ifaceAddr = null;
276                 }
277             }
278         } else {
279             // PANU Role = reverse Tether
280             if (state == BluetoothPan.STATE_CONNECTED) {
281                 BluetoothTetheringDataTracker.getInstance().startReverseTether(iface, device);
282             } else if (state == BluetoothPan.STATE_DISCONNECTED &&
283                   (prevState == BluetoothPan.STATE_CONNECTED ||
284                   prevState == BluetoothPan.STATE_DISCONNECTING)) {
285                 BluetoothTetheringDataTracker.getInstance().stopReverseTether(panDevice.mIface);
286             }
287         }
288 
289         if (panDevice == null) {
290             panDevice = new BluetoothPanDevice(state, ifaceAddr, iface, role);
291             mPanDevices.put(device, panDevice);
292         } else {
293             panDevice.mState = state;
294             panDevice.mIfaceAddr = ifaceAddr;
295             panDevice.mLocalRole = role;
296             panDevice.mIface = iface;
297         }
298 
299         Intent intent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
300         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
301         intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_STATE, prevState);
302         intent.putExtra(BluetoothPan.EXTRA_STATE, state);
303         intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, role);
304         mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM);
305 
306         debugLog("Pan Device state : device: " + device + " State:" + prevState + "->" + state);
307         mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.PAN, state,
308                                                     prevState);
309     }
310 
311     private class BluetoothPanDevice {
312         private int mState;
313         private String mIfaceAddr;
314         private String mIface;
315         private int mLocalRole; // Which local role is this PAN device bound to
316 
BluetoothPanDevice(int state, String ifaceAddr, String iface, int localRole)317         BluetoothPanDevice(int state, String ifaceAddr, String iface, int localRole) {
318             mState = state;
319             mIfaceAddr = ifaceAddr;
320             mIface = iface;
321             mLocalRole = localRole;
322         }
323     }
324 
createNewTetheringAddressLocked()325     private String createNewTetheringAddressLocked() {
326         if (getConnectedPanDevices().size() == mMaxPanDevices) {
327             debugLog ("Max PAN device connections reached");
328             return null;
329         }
330         String address = BLUETOOTH_IFACE_ADDR_START;
331         while (true) {
332             if (mBluetoothIfaceAddresses.contains(address)) {
333                 String[] addr = address.split("\\.");
334                 Integer newIp = Integer.parseInt(addr[2]) + 1;
335                 address = address.replace(addr[2], newIp.toString());
336             } else {
337                 break;
338             }
339         }
340         mBluetoothIfaceAddresses.add(address);
341         return address;
342     }
343 
344     // configured when we start tethering
enableTethering(String iface)345     private String enableTethering(String iface) {
346         debugLog("updateTetherState:" + iface);
347 
348         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
349         INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
350         ConnectivityManager cm =
351             (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
352         String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
353 
354         // bring toggle the interfaces
355         String[] currentIfaces = new String[0];
356         try {
357             currentIfaces = service.listInterfaces();
358         } catch (Exception e) {
359             Log.e(TAG, "Error listing Interfaces :" + e);
360             return null;
361         }
362 
363         boolean found = false;
364         for (String currIface: currentIfaces) {
365             if (currIface.equals(iface)) {
366                 found = true;
367                 break;
368             }
369         }
370 
371         if (!found) return null;
372 
373         String address = createNewTetheringAddressLocked();
374         if (address == null) return null;
375 
376         InterfaceConfiguration ifcg = null;
377         try {
378             ifcg = service.getInterfaceConfig(iface);
379             if (ifcg != null) {
380                 final LinkAddress linkAddr = ifcg.getLinkAddress();
381                 InetAddress addr = null;
382                 if (linkAddr == null || (addr = linkAddr.getAddress()) == null ||
383                         addr.equals(NetworkUtils.numericToInetAddress("0.0.0.0")) ||
384                         addr.equals(NetworkUtils.numericToInetAddress("::0"))) {
385                     addr = NetworkUtils.numericToInetAddress(address);
386                 }
387                 ifcg.setInterfaceUp();
388                 ifcg.clearFlag("running");
389                 ifcg.setLinkAddress(new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH));
390                 service.setInterfaceConfig(iface, ifcg);
391                 if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
392                     Log.e(TAG, "Error tethering "+iface);
393                 }
394             }
395         } catch (Exception e) {
396             Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
397             return null;
398         }
399         return address;
400     }
401 
debugLog(String msg)402     private static void debugLog(String msg) {
403         if (DBG) Log.d(TAG, msg);
404     }
405 
errorLog(String msg)406     private static void errorLog(String msg) {
407         Log.e(TAG, msg);
408     }
409 }
410