• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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.bluetooth.BluetoothPan;
21 import android.bluetooth.BluetoothProfile;
22 import android.bluetooth.IBluetoothPan;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.res.Resources.NotFoundException;
26 import android.net.ConnectivityManager;
27 import android.net.InterfaceConfiguration;
28 import android.net.LinkAddress;
29 import android.net.NetworkUtils;
30 import android.os.Handler;
31 import android.os.IBinder;
32 import android.os.Binder;
33 import android.os.INetworkManagementService;
34 import android.os.Message;
35 import android.os.ServiceManager;
36 import android.os.UserManager;
37 import android.provider.Settings;
38 import android.util.Log;
39 
40 import com.android.bluetooth.btservice.ProfileService;
41 import com.android.bluetooth.Utils;
42 
43 import java.net.InetAddress;
44 import java.util.ArrayList;
45 import java.util.HashMap;
46 import java.util.List;
47 
48 /**
49  * Provides Bluetooth Pan Device profile, as a service in
50  * the Bluetooth application.
51  * @hide
52  */
53 public class PanService extends ProfileService {
54     private static final String TAG = "PanService";
55     private static final boolean DBG = false;
56     private static PanService sPanService;
57 
58     private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1";
59     private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5;
60     private static final int BLUETOOTH_PREFIX_LENGTH        = 24;
61 
62     private HashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices;
63     private ArrayList<String> mBluetoothIfaceAddresses;
64     private int mMaxPanDevices;
65     private String mPanIfName;
66     private String mNapIfaceAddr;
67     private boolean mNativeAvailable;
68 
69     private static final int MESSAGE_CONNECT = 1;
70     private static final int MESSAGE_DISCONNECT = 2;
71     private static final int MESSAGE_CONNECT_STATE_CHANGED = 11;
72     private boolean mTetherOn = false;
73 
74     private BluetoothTetheringNetworkFactory mNetworkFactory;
75 
76 
77     static {
classInitNative()78         classInitNative();
79     }
80 
getName()81     protected String getName() {
82         return TAG;
83     }
84 
initBinder()85     public IProfileServiceBinder initBinder() {
86         return new BluetoothPanBinder(this);
87     }
88 
getPanService()89     public static synchronized PanService getPanService() {
90         if (sPanService != null && sPanService.isAvailable()) {
91             if (DBG) {
92                 Log.d(TAG, "getPanService(): returning " + sPanService);
93             }
94             return sPanService;
95         }
96         if (DBG) {
97             if (sPanService == null) {
98                 Log.d(TAG, "getPanService(): service is NULL");
99             } else if (!sPanService.isAvailable()) {
100                 Log.d(TAG, "getPanService(): service is not available");
101             }
102         }
103         return null;
104     }
105 
setPanService(PanService instance)106     private static synchronized void setPanService(PanService instance) {
107         if (instance != null && instance.isAvailable()) {
108             if (DBG) {
109                 Log.d(TAG, "setPanService(): set to: " + instance);
110             }
111             sPanService = instance;
112         } else {
113             if (DBG) {
114                 if (instance == null) {
115                     Log.d(TAG, "setPanService(): service not available");
116                 } else if (!instance.isAvailable()) {
117                     Log.d(TAG, "setPanService(): service is cleaning up");
118                 }
119             }
120         }
121     }
122 
start()123     protected boolean start() {
124         mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>();
125         mBluetoothIfaceAddresses = new ArrayList<String>();
126         try {
127             mMaxPanDevices = getResources().getInteger(
128                                  com.android.internal.R.integer.config_max_pan_devices);
129         } catch (NotFoundException e) {
130             mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS;
131         }
132         initializeNative();
133         mNativeAvailable=true;
134 
135         mNetworkFactory = new BluetoothTetheringNetworkFactory(getBaseContext(), getMainLooper(),
136                 this);
137         setPanService(this);
138 
139         return true;
140     }
141 
stop()142     protected boolean stop() {
143         mHandler.removeCallbacksAndMessages(null);
144         return true;
145     }
146 
cleanup()147     protected boolean cleanup() {
148         if (mNativeAvailable) {
149             cleanupNative();
150             mNativeAvailable=false;
151         }
152         if(mPanDevices != null) {
153             List<BluetoothDevice> DevList = getConnectedDevices();
154             for(BluetoothDevice dev : DevList) {
155                 handlePanDeviceStateChange(dev, mPanIfName, BluetoothProfile.STATE_DISCONNECTED,
156                         BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE);
157             }
158             mPanDevices.clear();
159         }
160         return true;
161     }
162 
163     private final Handler mHandler = new Handler() {
164         @Override
165         public void handleMessage(Message msg) {
166             switch (msg.what) {
167                 case MESSAGE_CONNECT:
168                 {
169                     BluetoothDevice device = (BluetoothDevice) msg.obj;
170                     if (!connectPanNative(Utils.getByteAddress(device),
171                             BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE)) {
172                         handlePanDeviceStateChange(device, null, BluetoothProfile.STATE_CONNECTING,
173                                 BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE);
174                         handlePanDeviceStateChange(device, null,
175                                 BluetoothProfile.STATE_DISCONNECTED, BluetoothPan.LOCAL_PANU_ROLE,
176                                 BluetoothPan.REMOTE_NAP_ROLE);
177                         break;
178                     }
179                 }
180                     break;
181                 case MESSAGE_DISCONNECT:
182                 {
183                     BluetoothDevice device = (BluetoothDevice) msg.obj;
184                     if (!disconnectPanNative(Utils.getByteAddress(device)) ) {
185                         handlePanDeviceStateChange(device, mPanIfName,
186                                 BluetoothProfile.STATE_DISCONNECTING, BluetoothPan.LOCAL_PANU_ROLE,
187                                 BluetoothPan.REMOTE_NAP_ROLE);
188                         handlePanDeviceStateChange(device, mPanIfName,
189                                 BluetoothProfile.STATE_DISCONNECTED, BluetoothPan.LOCAL_PANU_ROLE,
190                                 BluetoothPan.REMOTE_NAP_ROLE);
191                         break;
192                     }
193                 }
194                     break;
195                 case MESSAGE_CONNECT_STATE_CHANGED:
196                 {
197                     ConnectState cs = (ConnectState)msg.obj;
198                     BluetoothDevice device = getDevice(cs.addr);
199                     // TBD get iface from the msg
200                     if (DBG) {
201                         log("MESSAGE_CONNECT_STATE_CHANGED: " + device + " state: " + cs.state);
202                     }
203                     handlePanDeviceStateChange(device, mPanIfName /* iface */,
204                             convertHalState(cs.state), cs.local_role,  cs.remote_role);
205                 }
206                 break;
207             }
208         }
209     };
210 
211     /**
212      * Handlers for incoming service calls
213      */
214     private static class BluetoothPanBinder extends IBluetoothPan.Stub
215             implements IProfileServiceBinder {
216         private PanService mService;
BluetoothPanBinder(PanService svc)217         public BluetoothPanBinder(PanService svc) {
218             mService = svc;
219         }
cleanup()220         public boolean cleanup() {
221             mService = null;
222             return true;
223         }
getService()224         private PanService getService() {
225             if (!Utils.checkCaller()) {
226                 Log.w(TAG,"Pan call not allowed for non-active user");
227                 return null;
228             }
229 
230             if (mService  != null && mService.isAvailable()) {
231                 return mService;
232             }
233             return null;
234         }
connect(BluetoothDevice device)235         public boolean connect(BluetoothDevice device) {
236             PanService service = getService();
237             if (service == null) return false;
238             return service.connect(device);
239         }
disconnect(BluetoothDevice device)240         public boolean disconnect(BluetoothDevice device) {
241             PanService service = getService();
242             if (service == null) return false;
243             return service.disconnect(device);
244         }
getConnectionState(BluetoothDevice device)245         public int getConnectionState(BluetoothDevice device) {
246             PanService service = getService();
247             if (service == null) return BluetoothPan.STATE_DISCONNECTED;
248             return service.getConnectionState(device);
249         }
isPanNapOn()250         private boolean isPanNapOn() {
251             PanService service = getService();
252             if (service == null) return false;
253             return service.isPanNapOn();
254         }
isPanUOn()255         private boolean isPanUOn() {
256             if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative");
257             PanService service = getService();
258             if (service == null) return false;
259             return service.isPanUOn();
260         }
isTetheringOn()261         public boolean isTetheringOn() {
262             // TODO(BT) have a variable marking the on/off state
263             PanService service = getService();
264             if (service == null) return false;
265             return service.isTetheringOn();
266         }
setBluetoothTethering(boolean value)267         public void setBluetoothTethering(boolean value) {
268             PanService service = getService();
269             if (service == null) return;
270             Log.d(TAG, "setBluetoothTethering: " + value +", mTetherOn: " + service.mTetherOn);
271             service.setBluetoothTethering(value);
272         }
273 
getConnectedDevices()274         public List<BluetoothDevice> getConnectedDevices() {
275             PanService service = getService();
276             if (service == null) return new ArrayList<BluetoothDevice>(0);
277             return service.getConnectedDevices();
278         }
279 
getDevicesMatchingConnectionStates(int[] states)280         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
281             PanService service = getService();
282             if (service == null) return new ArrayList<BluetoothDevice>(0);
283             return service.getDevicesMatchingConnectionStates(states);
284         }
285     };
286 
connect(BluetoothDevice device)287     public boolean connect(BluetoothDevice device) {
288         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
289         if (getConnectionState(device) != BluetoothProfile.STATE_DISCONNECTED) {
290             Log.e(TAG, "Pan Device not disconnected: " + device);
291             return false;
292         }
293         Message msg = mHandler.obtainMessage(MESSAGE_CONNECT,device);
294         mHandler.sendMessage(msg);
295         return true;
296     }
297 
disconnect(BluetoothDevice device)298     public boolean disconnect(BluetoothDevice device) {
299         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
300         Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT,device);
301         mHandler.sendMessage(msg);
302         return true;
303     }
304 
getConnectionState(BluetoothDevice device)305     public int getConnectionState(BluetoothDevice device) {
306         BluetoothPanDevice panDevice = mPanDevices.get(device);
307         if (panDevice == null) {
308             return BluetoothPan.STATE_DISCONNECTED;
309         }
310         return panDevice.mState;
311     }
312 
isPanNapOn()313     boolean isPanNapOn() {
314         if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative");
315         return (getPanLocalRoleNative() & BluetoothPan.LOCAL_NAP_ROLE) != 0;
316     }
isPanUOn()317     boolean isPanUOn() {
318         if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative");
319         return (getPanLocalRoleNative() & BluetoothPan.LOCAL_PANU_ROLE) != 0;
320     }
isTetheringOn()321     public boolean isTetheringOn() {
322         // TODO(BT) have a variable marking the on/off state
323         return mTetherOn;
324     }
325 
setBluetoothTethering(boolean value)326     void setBluetoothTethering(boolean value) {
327         if(DBG) Log.d(TAG, "setBluetoothTethering: " + value +", mTetherOn: " + mTetherOn);
328         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
329         final Context context = getBaseContext();
330         String pkgName = context.getOpPackageName();
331 
332         // Clear caller identity temporarily so enforceTetherChangePermission UID checks work
333         // correctly
334         final long identityToken = Binder.clearCallingIdentity();
335         try {
336             ConnectivityManager.enforceTetherChangePermission(context, pkgName);
337         } finally {
338             Binder.restoreCallingIdentity(identityToken);
339         }
340 
341         UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
342         if (um.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) {
343             throw new SecurityException("DISALLOW_CONFIG_TETHERING is enabled for this user.");
344         }
345         if(mTetherOn != value) {
346             //drop any existing panu or pan-nap connection when changing the tethering state
347             mTetherOn = value;
348             List<BluetoothDevice> DevList = getConnectedDevices();
349             for(BluetoothDevice dev : DevList)
350                 disconnect(dev);
351         }
352     }
353 
setPriority(BluetoothDevice device, int priority)354     public boolean setPriority(BluetoothDevice device, int priority) {
355         if (device == null) {
356             throw new IllegalArgumentException("Null device");
357         }
358         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
359         Settings.Global.putInt(getContentResolver(),
360                 Settings.Global.getBluetoothPanPriorityKey(device.getAddress()),
361                 priority);
362         if (DBG) {
363             Log.d(TAG,"Saved priority " + device + " = " + priority);
364         }
365         return true;
366     }
367 
getPriority(BluetoothDevice device)368     public int getPriority(BluetoothDevice device) {
369         if (device == null) throw new IllegalArgumentException("Null device");
370         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
371         return Settings.Global.getInt(getContentResolver(),
372                 Settings.Global.getBluetoothPanPriorityKey(device.getAddress()),
373                 BluetoothProfile.PRIORITY_UNDEFINED);
374     }
375 
getConnectedDevices()376     public List<BluetoothDevice> getConnectedDevices() {
377         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
378         List<BluetoothDevice> devices = getDevicesMatchingConnectionStates(
379                 new int[] {BluetoothProfile.STATE_CONNECTED});
380         return devices;
381     }
382 
getDevicesMatchingConnectionStates(int[] states)383     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
384          enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
385         List<BluetoothDevice> panDevices = new ArrayList<BluetoothDevice>();
386 
387         for (BluetoothDevice device: mPanDevices.keySet()) {
388             int panDeviceState = getConnectionState(device);
389             for (int state : states) {
390                 if (state == panDeviceState) {
391                     panDevices.add(device);
392                     break;
393                 }
394             }
395         }
396         return panDevices;
397     }
398 
399     static protected class ConnectState {
ConnectState(byte[] address, int state, int error, int local_role, int remote_role)400         public ConnectState(byte[] address, int state, int error, int local_role, int remote_role) {
401             this.addr = address;
402             this.state = state;
403             this.error = error;
404             this.local_role = local_role;
405             this.remote_role = remote_role;
406         }
407         byte[] addr;
408         int state;
409         int error;
410         int local_role;
411         int remote_role;
412     };
onConnectStateChanged(byte[] address, int state, int error, int local_role, int remote_role)413     private void onConnectStateChanged(byte[] address, int state, int error, int local_role,
414             int remote_role) {
415         if (DBG) {
416             log("onConnectStateChanged: " + state + ", local role:" + local_role +
417                     ", remote_role: " + remote_role);
418         }
419         Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
420         msg.obj = new ConnectState(address, state, error, local_role, remote_role);
421         mHandler.sendMessage(msg);
422     }
onControlStateChanged(int local_role, int state, int error, String ifname)423     private void onControlStateChanged(int local_role, int state, int error, String ifname) {
424         if (DBG)
425             log("onControlStateChanged: " + state + ", error: " + error + ", ifname: " + ifname);
426         if(error == 0)
427             mPanIfName = ifname;
428     }
429 
convertHalState(int halState)430     private static int convertHalState(int halState) {
431         switch (halState) {
432             case CONN_STATE_CONNECTED:
433                 return BluetoothProfile.STATE_CONNECTED;
434             case CONN_STATE_CONNECTING:
435                 return BluetoothProfile.STATE_CONNECTING;
436             case CONN_STATE_DISCONNECTED:
437                 return BluetoothProfile.STATE_DISCONNECTED;
438             case CONN_STATE_DISCONNECTING:
439                 return BluetoothProfile.STATE_DISCONNECTING;
440             default:
441                 Log.e(TAG, "bad pan connection state: " + halState);
442                 return BluetoothProfile.STATE_DISCONNECTED;
443         }
444     }
445 
handlePanDeviceStateChange(BluetoothDevice device, String iface, int state, int local_role, int remote_role)446     void handlePanDeviceStateChange(BluetoothDevice device,
447                                     String iface, int state, int local_role, int remote_role) {
448         if(DBG) {
449             Log.d(TAG, "handlePanDeviceStateChange: device: " + device + ", iface: " + iface +
450                     ", state: " + state + ", local_role:" + local_role + ", remote_role:" +
451                     remote_role);
452         }
453         int prevState;
454 
455         BluetoothPanDevice panDevice = mPanDevices.get(device);
456         if (panDevice == null) {
457             Log.i(TAG, "state " + state + " Num of connected pan devices: " + mPanDevices.size());
458             prevState = BluetoothProfile.STATE_DISCONNECTED;
459             panDevice = new BluetoothPanDevice(state, iface, local_role);
460             mPanDevices.put(device, panDevice);
461         } else {
462             prevState = panDevice.mState;
463             panDevice.mState = state;
464             panDevice.mLocalRole = local_role;
465             panDevice.mIface = iface;
466         }
467 
468         // Avoid race condition that gets this class stuck in STATE_DISCONNECTING. While we
469         // are in STATE_CONNECTING, if a BluetoothPan#disconnect call comes in, the original
470         // connect call will put us in STATE_DISCONNECTED. Then, the disconnect completes and
471         // changes the state to STATE_DISCONNECTING. All future calls to BluetoothPan#connect
472         // will fail until the caller explicitly calls BluetoothPan#disconnect.
473         if (prevState == BluetoothProfile.STATE_DISCONNECTED && state == BluetoothProfile.STATE_DISCONNECTING) {
474             Log.d(TAG, "Ignoring state change from " + prevState + " to " + state);
475             mPanDevices.remove(device);
476             return;
477         }
478 
479         Log.d(TAG, "handlePanDeviceStateChange preState: " + prevState + " state: " + state);
480         if (prevState == state) return;
481         if (remote_role == BluetoothPan.LOCAL_PANU_ROLE) {
482             if (state == BluetoothProfile.STATE_CONNECTED) {
483                 if ((!mTetherOn) || (local_role == BluetoothPan.LOCAL_PANU_ROLE)) {
484                     Log.d(TAG, "handlePanDeviceStateChange BT tethering is off/Local role"
485                             + " is PANU drop the connection");
486                     mPanDevices.remove(device);
487                     disconnectPanNative(Utils.getByteAddress(device));
488                     return;
489                 }
490                 Log.d(TAG, "handlePanDeviceStateChange LOCAL_NAP_ROLE:REMOTE_PANU_ROLE");
491                 if (mNapIfaceAddr == null) {
492                     mNapIfaceAddr = startTethering(iface);
493                     if (mNapIfaceAddr == null) {
494                         Log.e(TAG, "Error seting up tether interface");
495                         mPanDevices.remove(device);
496                         disconnectPanNative(Utils.getByteAddress(device));
497                         return;
498                     }
499                 }
500             } else if (state == BluetoothProfile.STATE_DISCONNECTED) {
501                 mPanDevices.remove(device);
502                 Log.i(TAG, "remote(PANU) is disconnected, Remaining connected PANU devices: "
503                         + mPanDevices.size());
504                 if (mNapIfaceAddr != null && mPanDevices.size() == 0) {
505                     stopTethering(iface);
506                     mNapIfaceAddr = null;
507                 }
508             }
509         } else if (mNetworkFactory != null) {
510             // PANU Role = reverse Tether
511             Log.d(TAG, "handlePanDeviceStateChange LOCAL_PANU_ROLE:REMOTE_NAP_ROLE state = " +
512                     state + ", prevState = " + prevState);
513             if (state == BluetoothProfile.STATE_CONNECTED) {
514                 mNetworkFactory.startReverseTether(iface);
515            } else if (state == BluetoothProfile.STATE_DISCONNECTED &&
516                    (prevState == BluetoothProfile.STATE_CONNECTED ||
517                    prevState == BluetoothProfile.STATE_DISCONNECTING)) {
518                 mNetworkFactory.stopReverseTether();
519                 mPanDevices.remove(device);
520             }
521         }
522 
523         /* Notifying the connection state change of the profile before sending the intent for
524            connection state change, as it was causing a race condition, with the UI not being
525            updated with the correct connection state. */
526         Log.d(TAG, "Pan Device state : device: " + device + " State:" +
527                        prevState + "->" + state);
528         Intent intent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
529         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
530         intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_STATE, prevState);
531         intent.putExtra(BluetoothPan.EXTRA_STATE, state);
532         intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, local_role);
533         sendBroadcast(intent, BLUETOOTH_PERM);
534     }
535 
startTethering(String iface)536     private String startTethering(String iface) {
537         return configureBtIface(true, iface);
538     }
539 
stopTethering(String iface)540     private String stopTethering(String iface) {
541         return configureBtIface(false, iface);
542     }
543 
configureBtIface(boolean enable, String iface)544     private String configureBtIface(boolean enable, String iface) {
545         Log.i(TAG, "configureBtIface: " + iface + " enable: " + enable);
546 
547         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
548         INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
549         ConnectivityManager cm =
550             (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
551         String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
552 
553         // bring toggle the interfaces
554         String[] currentIfaces = new String[0];
555         try {
556             currentIfaces = service.listInterfaces();
557         } catch (Exception e) {
558             Log.e(TAG, "Error listing Interfaces :" + e);
559             return null;
560         }
561 
562         boolean found = false;
563         for (String currIface: currentIfaces) {
564             if (currIface.equals(iface)) {
565                 found = true;
566                 break;
567             }
568         }
569 
570         if (!found) return null;
571 
572         InterfaceConfiguration ifcg = null;
573         String address = null;
574         try {
575             ifcg = service.getInterfaceConfig(iface);
576             if (ifcg != null) {
577                 InetAddress addr = null;
578                 LinkAddress linkAddr = ifcg.getLinkAddress();
579                 if (linkAddr == null || (addr = linkAddr.getAddress()) == null ||
580                         addr.equals(NetworkUtils.numericToInetAddress("0.0.0.0")) ||
581                         addr.equals(NetworkUtils.numericToInetAddress("::0"))) {
582                     address = BLUETOOTH_IFACE_ADDR_START;
583                     addr = NetworkUtils.numericToInetAddress(address);
584                 }
585 
586                 ifcg.setLinkAddress(new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH));
587                 if (enable) {
588                     ifcg.setInterfaceUp();
589                 } else {
590                     ifcg.setInterfaceDown();
591                 }
592 
593                 ifcg.clearFlag("running");
594                 service.setInterfaceConfig(iface, ifcg);
595 
596                 if (enable) {
597                     int tetherStatus = cm.tether(iface);
598                     if (tetherStatus != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
599                         Log.e(TAG, "Error tethering "+ iface + " tetherStatus: " + tetherStatus);
600                         return null;
601                     }
602                 } else {
603                     int untetherStatus = cm.untether(iface);
604                     Log.i(TAG, "Untethered: "+ iface + " untetherStatus: " + untetherStatus);
605                 }
606             }
607         } catch (Exception e) {
608             Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
609             return null;
610         }
611         return address;
612     }
613 
getConnectedPanDevices()614     private List<BluetoothDevice> getConnectedPanDevices() {
615         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
616 
617         for (BluetoothDevice device: mPanDevices.keySet()) {
618             if (getPanDeviceConnectionState(device) == BluetoothProfile.STATE_CONNECTED) {
619                 devices.add(device);
620             }
621         }
622         return devices;
623     }
624 
getPanDeviceConnectionState(BluetoothDevice device)625     private int getPanDeviceConnectionState(BluetoothDevice device) {
626         BluetoothPanDevice panDevice = mPanDevices.get(device);
627         if (panDevice == null) {
628             return BluetoothProfile.STATE_DISCONNECTED;
629         }
630         return panDevice.mState;
631     }
632 
633     @Override
dump(StringBuilder sb)634     public void dump(StringBuilder sb) {
635         super.dump(sb);
636         println(sb, "mMaxPanDevices: " + mMaxPanDevices);
637         println(sb, "mPanIfName: " + mPanIfName);
638         println(sb, "mTetherOn: " + mTetherOn);
639         println(sb, "mPanDevices:");
640         for (BluetoothDevice device : mPanDevices.keySet()) {
641             println(sb, "  " + device + " : " + mPanDevices.get(device));
642         }
643     }
644 
645     private class BluetoothPanDevice {
646         private int mState;
647         private String mIface;
648         private int mLocalRole; // Which local role is this PAN device bound to
649 
BluetoothPanDevice(int state, String iface, int localRole)650         BluetoothPanDevice(int state, String iface, int localRole) {
651             mState = state;
652             mIface = iface;
653             mLocalRole = localRole;
654         }
655     }
656 
657     // Constants matching Hal header file bt_hh.h
658     // bthh_connection_state_t
659     private final static int CONN_STATE_CONNECTED = 0;
660     private final static int CONN_STATE_CONNECTING = 1;
661     private final static int CONN_STATE_DISCONNECTED = 2;
662     private final static int CONN_STATE_DISCONNECTING = 3;
663 
classInitNative()664     private native static void classInitNative();
initializeNative()665     private native void initializeNative();
cleanupNative()666     private native void cleanupNative();
connectPanNative(byte[] btAddress, int local_role, int remote_role)667     private native boolean connectPanNative(byte[] btAddress, int local_role, int remote_role);
disconnectPanNative(byte[] btAddress)668     private native boolean disconnectPanNative(byte[] btAddress);
enablePanNative(int local_role)669     private native boolean enablePanNative(int local_role);
getPanLocalRoleNative()670     private native int getPanLocalRoleNative();
671 
672 }
673