• 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 static android.Manifest.permission.BLUETOOTH_CONNECT;
20 import static android.Manifest.permission.TETHER_PRIVILEGED;
21 
22 import android.annotation.RequiresPermission;
23 import android.bluetooth.BluetoothDevice;
24 import android.bluetooth.BluetoothPan;
25 import android.bluetooth.BluetoothPan.LocalPanRole;
26 import android.bluetooth.BluetoothPan.RemotePanRole;
27 import android.bluetooth.BluetoothProfile;
28 import android.bluetooth.IBluetoothPan;
29 import android.bluetooth.IBluetoothPanCallback;
30 import android.content.AttributionSource;
31 import android.content.Intent;
32 import android.content.res.Resources.NotFoundException;
33 import android.net.TetheringInterface;
34 import android.net.TetheringManager;
35 import android.os.Handler;
36 import android.os.Looper;
37 import android.os.Message;
38 import android.os.RemoteException;
39 import android.os.UserManager;
40 import android.sysprop.BluetoothProperties;
41 import android.util.Log;
42 
43 import com.android.bluetooth.BluetoothMetricsProto;
44 import com.android.bluetooth.Utils;
45 import com.android.bluetooth.btservice.AdapterService;
46 import com.android.bluetooth.btservice.MetricsLogger;
47 import com.android.bluetooth.btservice.ProfileService;
48 import com.android.bluetooth.btservice.storage.DatabaseManager;
49 import com.android.internal.annotations.VisibleForTesting;
50 import com.android.modules.utils.HandlerExecutor;
51 import com.android.modules.utils.SynchronousResultReceiver;
52 
53 import java.util.ArrayList;
54 import java.util.HashMap;
55 import java.util.List;
56 import java.util.Map;
57 import java.util.Objects;
58 
59 /**
60  * Provides Bluetooth Pan Device profile, as a service in
61  * the Bluetooth application.
62  * @hide
63  */
64 public class PanService extends ProfileService {
65     private static final String TAG = "PanService";
66     private static final boolean DBG = false;
67     private static PanService sPanService;
68 
69     private static final String BLUETOOTH_IFACE_ADDR_START = "192.168.44.1";
70     private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5;
71     private static final int BLUETOOTH_PREFIX_LENGTH = 24;
72 
73     @VisibleForTesting
74     HashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices;
75     private int mMaxPanDevices;
76     private String mPanIfName;
77     @VisibleForTesting
78     boolean mIsTethering = false;
79     private boolean mNativeAvailable;
80     private HashMap<String, IBluetoothPanCallback> mBluetoothTetheringCallbacks;
81 
82     private TetheringManager mTetheringManager;
83     private DatabaseManager mDatabaseManager;
84     @VisibleForTesting
85     UserManager mUserManager;
86 
87     private static final int MESSAGE_CONNECT = 1;
88     private static final int MESSAGE_DISCONNECT = 2;
89     private static final int MESSAGE_CONNECT_STATE_CHANGED = 11;
90     private boolean mTetherOn = false;
91 
92     private BluetoothTetheringNetworkFactory mNetworkFactory;
93     private boolean mStarted = false;
94 
95     private AdapterService mAdapterService;
96 
97     TetheringManager.TetheringEventCallback mTetheringCallback =
98             new TetheringManager.TetheringEventCallback() {
99                 @Override
100                 public void onError(TetheringInterface iface, int error) {
101                     if (mIsTethering
102                             && iface.getType() == TetheringManager.TETHERING_BLUETOOTH) {
103                         // tethering is fail because of @TetheringIfaceError error.
104                         Log.e(TAG, "Error setting up tether interface: " + error);
105                         for (Map.Entry device : mPanDevices.entrySet()) {
106                             disconnectPanNative(Utils.getByteAddress(
107                                     (BluetoothDevice) device.getKey()));
108                         }
109                         mPanDevices.clear();
110                         mIsTethering = false;
111                     }
112                 }
113             };
114 
115     static {
classInitNative()116         classInitNative();
117     }
118 
isEnabled()119     public static boolean isEnabled() {
120         return BluetoothProperties.isProfilePanNapEnabled().orElse(false)
121                 || BluetoothProperties.isProfilePanPanuEnabled().orElse(false);
122     }
123 
124     @Override
initBinder()125     public IProfileServiceBinder initBinder() {
126         return new BluetoothPanBinder(this);
127     }
128 
getPanService()129     public static synchronized PanService getPanService() {
130         if (sPanService == null) {
131             Log.w(TAG, "getPanService(): service is null");
132             return null;
133         }
134         if (!sPanService.isAvailable()) {
135             Log.w(TAG, "getPanService(): service is not available ");
136             return null;
137         }
138         return sPanService;
139     }
140 
setPanService(PanService instance)141     private static synchronized void setPanService(PanService instance) {
142         if (DBG) {
143             Log.d(TAG, "setPanService(): set to: " + instance);
144         }
145         sPanService = instance;
146     }
147 
148     @Override
start()149     protected boolean start() {
150         mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
151                 "AdapterService cannot be null when PanService starts");
152         mDatabaseManager = Objects.requireNonNull(AdapterService.getAdapterService().getDatabase(),
153                 "DatabaseManager cannot be null when PanService starts");
154 
155         mBluetoothTetheringCallbacks = new HashMap<>();
156         mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>();
157         try {
158             mMaxPanDevices = getResources().getInteger(
159                     com.android.bluetooth.R.integer.config_max_pan_devices);
160         } catch (NotFoundException e) {
161             mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS;
162         }
163         initializeNative();
164         mNativeAvailable = true;
165 
166         mUserManager = getSystemService(UserManager.class);
167 
168         mTetheringManager = getSystemService(TetheringManager.class);
169         mTetheringManager.registerTetheringEventCallback(
170                 new HandlerExecutor(new Handler(Looper.getMainLooper())), mTetheringCallback);
171         setPanService(this);
172         mStarted = true;
173 
174         return true;
175     }
176 
177     @Override
stop()178     protected boolean stop() {
179         if (!mStarted) {
180             Log.w(TAG, "stop() called before start()");
181             return true;
182         }
183         mAdapterService = null;
184         if (mTetheringManager != null) {
185             mTetheringManager.unregisterTetheringEventCallback(mTetheringCallback);
186             mTetheringManager = null;
187         }
188         mHandler.removeCallbacksAndMessages(null);
189         return true;
190     }
191 
192     @Override
cleanup()193     protected void cleanup() {
194         // TODO(b/72948646): this should be moved to stop()
195         setPanService(null);
196         if (mNativeAvailable) {
197             cleanupNative();
198             mNativeAvailable = false;
199         }
200 
201         mUserManager = null;
202 
203         if (mPanDevices != null) {
204            int[] desiredStates = {BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED,
205                                   BluetoothProfile.STATE_DISCONNECTING};
206            List<BluetoothDevice> devList =
207                    getDevicesMatchingConnectionStates(desiredStates);
208            for (BluetoothDevice device : devList) {
209                 BluetoothPanDevice panDevice = mPanDevices.get(device);
210                 Log.d(TAG, "panDevice: " + panDevice + " device address: " + device);
211                 if (panDevice != null) {
212                     handlePanDeviceStateChange(device, mPanIfName,
213                         BluetoothProfile.STATE_DISCONNECTED,
214                         panDevice.mLocalRole, panDevice.mRemoteRole);
215                 }
216             }
217             mPanDevices.clear();
218         }
219     }
220 
221     private final Handler mHandler = new Handler() {
222         @Override
223         public void handleMessage(Message msg) {
224             switch (msg.what) {
225                 case MESSAGE_CONNECT: {
226                     BluetoothDevice device = (BluetoothDevice) msg.obj;
227                     if (!connectPanNative(mAdapterService.getByteIdentityAddress(device),
228                             BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE)) {
229                         handlePanDeviceStateChange(device, null, BluetoothProfile.STATE_CONNECTING,
230                                 BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE);
231                         handlePanDeviceStateChange(device, null,
232                                 BluetoothProfile.STATE_DISCONNECTED, BluetoothPan.LOCAL_PANU_ROLE,
233                                 BluetoothPan.REMOTE_NAP_ROLE);
234                         break;
235                     }
236                 }
237                 break;
238                 case MESSAGE_DISCONNECT: {
239                     BluetoothDevice device = (BluetoothDevice) msg.obj;
240                     if (!disconnectPanNative(mAdapterService.getByteIdentityAddress(device))) {
241                         handlePanDeviceStateChange(device, mPanIfName,
242                                 BluetoothProfile.STATE_DISCONNECTING, BluetoothPan.LOCAL_PANU_ROLE,
243                                 BluetoothPan.REMOTE_NAP_ROLE);
244                         handlePanDeviceStateChange(device, mPanIfName,
245                                 BluetoothProfile.STATE_DISCONNECTED, BluetoothPan.LOCAL_PANU_ROLE,
246                                 BluetoothPan.REMOTE_NAP_ROLE);
247                         break;
248                     }
249                 }
250                 break;
251                 case MESSAGE_CONNECT_STATE_CHANGED: {
252                     ConnectState cs = (ConnectState) msg.obj;
253                     final BluetoothDevice device = mAdapterService.getDeviceFromByte(cs.addr);
254                     // TBD get iface from the msg
255                     if (DBG) {
256                         Log.d(TAG,
257                                 "MESSAGE_CONNECT_STATE_CHANGED: " + device + " state: " + cs.state);
258                     }
259                     // It could be null if the connection up is coming when the Bluetooth is turning
260                     // off.
261                     if (device == null) {
262                         break;
263                     }
264                     handlePanDeviceStateChange(device, mPanIfName /* iface */,
265                             convertHalState(cs.state), cs.local_role, cs.remote_role);
266                 }
267                 break;
268             }
269         }
270     };
271 
272     /**
273      * Handlers for incoming service calls
274      */
275     @VisibleForTesting
276     static class BluetoothPanBinder extends IBluetoothPan.Stub
277             implements IProfileServiceBinder {
278         private PanService mService;
279 
BluetoothPanBinder(PanService svc)280         BluetoothPanBinder(PanService svc) {
281             mService = svc;
282         }
283 
284         @Override
cleanup()285         public void cleanup() {
286             mService = null;
287         }
288 
289         @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getService(AttributionSource source)290         private PanService getService(AttributionSource source) {
291             if (Utils.isInstrumentationTestMode()) {
292                 return mService;
293             }
294             if (!Utils.checkServiceAvailable(mService, TAG)
295                     || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)
296                     || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
297                 return null;
298             }
299             return mService;
300         }
301 
302         @Override
connect(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)303         public void connect(BluetoothDevice device, AttributionSource source,
304                 SynchronousResultReceiver receiver) {
305             try {
306                 PanService service = getService(source);
307                 boolean defaultValue = false;
308                 if (service != null) {
309                     defaultValue = service.connect(device);
310                 }
311                 receiver.send(defaultValue);
312             } catch (RuntimeException e) {
313                 receiver.propagateException(e);
314             }
315         }
316 
317         @Override
disconnect(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)318         public void disconnect(BluetoothDevice device, AttributionSource source,
319                 SynchronousResultReceiver receiver) {
320             try {
321                 PanService service = getService(source);
322                 boolean defaultValue = false;
323                 if (service != null) {
324                     defaultValue = service.disconnect(device);
325                 }
326                 receiver.send(defaultValue);
327             } catch (RuntimeException e) {
328                 receiver.propagateException(e);
329             }
330         }
331 
332         @Override
setConnectionPolicy(BluetoothDevice device, int connectionPolicy, AttributionSource source, SynchronousResultReceiver receiver)333         public void setConnectionPolicy(BluetoothDevice device, int connectionPolicy,
334                 AttributionSource source, SynchronousResultReceiver receiver) {
335             try {
336                 PanService service = getService(source);
337                 boolean defaultValue = false;
338                 if (service != null) {
339                     defaultValue = service.setConnectionPolicy(device, connectionPolicy);
340                 }
341                 receiver.send(defaultValue);
342             } catch (RuntimeException e) {
343                 receiver.propagateException(e);
344             }
345         }
346 
347         @Override
getConnectionState(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)348         public void getConnectionState(BluetoothDevice device, AttributionSource source,
349                 SynchronousResultReceiver receiver) {
350             try {
351                 PanService service = getService(source);
352                 int defaultValue = BluetoothPan.STATE_DISCONNECTED;
353                 if (service != null) {
354                     defaultValue = service.getConnectionState(device);
355                 }
356                 receiver.send(defaultValue);
357             } catch (RuntimeException e) {
358                 receiver.propagateException(e);
359             }
360         }
361 
362         @Override
isTetheringOn(AttributionSource source, SynchronousResultReceiver receiver)363         public void isTetheringOn(AttributionSource source, SynchronousResultReceiver receiver) {
364             // TODO(BT) have a variable marking the on/off state
365             try {
366                 PanService service = getService(source);
367                 boolean defaultValue = false;
368                 if (service != null) {
369                     defaultValue = service.isTetheringOn();
370                 }
371                 receiver.send(defaultValue);
372             } catch (RuntimeException e) {
373                 receiver.propagateException(e);
374             }
375         }
376 
377         @Override
setBluetoothTethering(IBluetoothPanCallback callback, int id, boolean value, AttributionSource source, SynchronousResultReceiver receiver)378         public void setBluetoothTethering(IBluetoothPanCallback callback, int id,
379                 boolean value, AttributionSource source,
380                 SynchronousResultReceiver receiver) {
381             try {
382                 PanService service = getService(source);
383                 if (service != null) {
384                     Log.d(TAG, "setBluetoothTethering: " + value
385                             + ", pkgName: " + source.getPackageName()
386                             + ", mTetherOn: " + service.mTetherOn);
387                     service.setBluetoothTethering(callback, id, source.getUid(), value);
388                 }
389                 receiver.send(null);
390             } catch (RuntimeException e) {
391                 receiver.propagateException(e);
392             }
393         }
394 
395         @Override
getConnectedDevices(AttributionSource source, SynchronousResultReceiver receiver)396         public void getConnectedDevices(AttributionSource source,
397                 SynchronousResultReceiver receiver) {
398             try {
399                 PanService service = getService(source);
400                 List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(0);
401                 if (service != null) {
402                     defaultValue = service.getConnectedDevices();
403                 }
404                 receiver.send(defaultValue);
405             } catch (RuntimeException e) {
406                 receiver.propagateException(e);
407             }
408         }
409 
410         @Override
getDevicesMatchingConnectionStates(int[] states, AttributionSource source, SynchronousResultReceiver receiver)411         public void getDevicesMatchingConnectionStates(int[] states,
412                 AttributionSource source, SynchronousResultReceiver receiver) {
413             try {
414                 PanService service = getService(source);
415                 List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(0);
416                 if (service != null) {
417                     defaultValue = service.getDevicesMatchingConnectionStates(states);
418                 }
419                 receiver.send(defaultValue);
420             } catch (RuntimeException e) {
421                 receiver.propagateException(e);
422             }
423         }
424     }
425 
426     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
connect(BluetoothDevice device)427     public boolean connect(BluetoothDevice device) {
428         if (mUserManager.isGuestUser()) {
429             Log.w(TAG, "Guest user does not have the permission to change the WiFi network");
430             return false;
431         }
432         if (getConnectionState(device) != BluetoothProfile.STATE_DISCONNECTED) {
433             Log.e(TAG, "Pan Device not disconnected: " + device);
434             return false;
435         }
436         Message msg = mHandler.obtainMessage(MESSAGE_CONNECT, device);
437         mHandler.sendMessage(msg);
438         return true;
439     }
440 
disconnect(BluetoothDevice device)441     public boolean disconnect(BluetoothDevice device) {
442         Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT, device);
443         mHandler.sendMessage(msg);
444         return true;
445     }
446 
447     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
getConnectionState(BluetoothDevice device)448     public int getConnectionState(BluetoothDevice device) {
449         enforceCallingOrSelfPermission(
450                 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
451         BluetoothPanDevice panDevice = mPanDevices.get(device);
452         if (panDevice == null) {
453             return BluetoothPan.STATE_DISCONNECTED;
454         }
455         return panDevice.mState;
456     }
457 
isTetheringOn()458     public boolean isTetheringOn() {
459         // TODO(BT) have a variable marking the on/off state
460         return mTetherOn;
461     }
462 
463     @RequiresPermission(allOf = {
464             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
465             android.Manifest.permission.TETHER_PRIVILEGED,
466     })
setBluetoothTethering(IBluetoothPanCallback callback, int id, int callerUid, boolean value)467     void setBluetoothTethering(IBluetoothPanCallback callback, int id, int callerUid,
468             boolean value) {
469         if (DBG) {
470             Log.d(TAG, "setBluetoothTethering: " + value + ", mTetherOn: " + mTetherOn);
471         }
472         enforceCallingOrSelfPermission(
473                 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
474         enforceCallingOrSelfPermission(
475                 TETHER_PRIVILEGED, "Need TETHER_PRIVILEGED permission");
476 
477         UserManager um = getSystemService(UserManager.class);
478         if (um.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING) && value) {
479             throw new SecurityException("DISALLOW_CONFIG_TETHERING is enabled for this user.");
480         }
481         final String key = id + "/" + callerUid;
482         if (callback != null) {
483             boolean keyExists = mBluetoothTetheringCallbacks.containsKey(key);
484             if (value) {
485                 if (!keyExists) {
486                     mBluetoothTetheringCallbacks.put(key, callback);
487                 } else {
488                     Log.e(TAG, "setBluetoothTethering Error: Callback already registered.");
489                     return;
490                 }
491             } else {
492                 if (keyExists) {
493                     mBluetoothTetheringCallbacks.remove(key);
494                 } else {
495                     Log.e(TAG, "setBluetoothTethering Error: Callback not registered.");
496                     return;
497                 }
498             }
499         }
500         if (mTetherOn != value) {
501             //drop any existing panu or pan-nap connection when changing the tethering state
502             mTetherOn = value;
503             List<BluetoothDevice> devList = getConnectedDevices();
504             for (BluetoothDevice dev : devList) {
505                 disconnect(dev);
506             }
507             Intent intent = new Intent(BluetoothPan.ACTION_TETHERING_STATE_CHANGED);
508             intent.putExtra(BluetoothPan.EXTRA_TETHERING_STATE,
509                     mTetherOn ? BluetoothPan.TETHERING_STATE_ON : BluetoothPan.TETHERING_STATE_OFF);
510             sendBroadcast(intent, null, Utils.getTempAllowlistBroadcastOptions());
511         }
512     }
513 
514     /**
515      * Set connection policy of the profile and connects it if connectionPolicy is
516      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is
517      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}
518      *
519      * <p> The device should already be paired.
520      * Connection policy can be one of:
521      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
522      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
523      * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
524      *
525      * @param device Paired bluetooth device
526      * @param connectionPolicy is the connection policy to set to for this profile
527      * @return true if connectionPolicy is set, false on error
528      */
529     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
setConnectionPolicy(BluetoothDevice device, int connectionPolicy)530     public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
531         enforceCallingOrSelfPermission(
532                 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
533         if (DBG) {
534             Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
535         }
536 
537         if (!mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.PAN,
538                   connectionPolicy)) {
539             return false;
540         }
541         if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
542             connect(device);
543         } else if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
544             disconnect(device);
545         }
546         return true;
547     }
548 
549     /**
550      * Get the connection policy of the profile.
551      *
552      * <p> The connection policy can be any of:
553      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
554      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
555      * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
556      *
557      * @param device Bluetooth device
558      * @return connection policy of the device
559      * @hide
560      */
getConnectionPolicy(BluetoothDevice device)561     public int getConnectionPolicy(BluetoothDevice device) {
562         return mDatabaseManager
563                 .getProfileConnectionPolicy(device, BluetoothProfile.PAN);
564     }
565 
566     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
getConnectedDevices()567     public List<BluetoothDevice> getConnectedDevices() {
568         enforceCallingOrSelfPermission(
569                 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
570         List<BluetoothDevice> devices =
571                 getDevicesMatchingConnectionStates(new int[]{BluetoothProfile.STATE_CONNECTED});
572         return devices;
573     }
574 
575     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
getDevicesMatchingConnectionStates(int[] states)576     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
577         List<BluetoothDevice> panDevices = new ArrayList<BluetoothDevice>();
578 
579         for (BluetoothDevice device : mPanDevices.keySet()) {
580             int panDeviceState = getConnectionState(device);
581             for (int state : states) {
582                 if (state == panDeviceState) {
583                     panDevices.add(device);
584                     break;
585                 }
586             }
587         }
588         return panDevices;
589     }
590 
591     protected static class ConnectState {
ConnectState(byte[] address, int state, int error, int localRole, int remoteRole)592         public ConnectState(byte[] address, int state, int error, int localRole, int remoteRole) {
593             this.addr = address;
594             this.state = state;
595             this.error = error;
596             this.local_role = localRole;
597             this.remote_role = remoteRole;
598         }
599 
600         public byte[] addr;
601         public int state;
602         public int error;
603         public int local_role;
604         public int remote_role;
605     }
606 
607     @VisibleForTesting
onConnectStateChanged(byte[] address, int state, int error, int localRole, int remoteRole)608     void onConnectStateChanged(byte[] address, int state, int error, int localRole,
609             int remoteRole) {
610         if (DBG) {
611             Log.d(TAG, "onConnectStateChanged: " + state + ", local role:" + localRole
612                     + ", remoteRole: " + remoteRole);
613         }
614         Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
615         msg.obj = new ConnectState(address, state, error, localRole, remoteRole);
616         mHandler.sendMessage(msg);
617     }
618 
619     @VisibleForTesting
onControlStateChanged(int localRole, int state, int error, String ifname)620     void onControlStateChanged(int localRole, int state, int error, String ifname) {
621         if (DBG) {
622             Log.d(TAG, "onControlStateChanged: " + state + ", error: " + error + ", ifname: "
623                     + ifname);
624         }
625         if (error == 0) {
626             mPanIfName = ifname;
627         }
628     }
629 
630     @VisibleForTesting
convertHalState(int halState)631     static int convertHalState(int halState) {
632         switch (halState) {
633             case CONN_STATE_CONNECTED:
634                 return BluetoothProfile.STATE_CONNECTED;
635             case CONN_STATE_CONNECTING:
636                 return BluetoothProfile.STATE_CONNECTING;
637             case CONN_STATE_DISCONNECTED:
638                 return BluetoothProfile.STATE_DISCONNECTED;
639             case CONN_STATE_DISCONNECTING:
640                 return BluetoothProfile.STATE_DISCONNECTING;
641             default:
642                 Log.e(TAG, "bad pan connection state: " + halState);
643                 return BluetoothProfile.STATE_DISCONNECTED;
644         }
645     }
646 
handlePanDeviceStateChange(BluetoothDevice device, String iface, int state, @LocalPanRole int localRole, @RemotePanRole int remoteRole)647     void handlePanDeviceStateChange(BluetoothDevice device, String iface, int state,
648             @LocalPanRole int localRole, @RemotePanRole int remoteRole) {
649         if (DBG) {
650             Log.d(TAG, "handlePanDeviceStateChange: device: " + device + ", iface: " + iface
651                     + ", state: " + state + ", localRole:" + localRole + ", remoteRole:"
652                     + remoteRole);
653         }
654         int prevState;
655 
656         BluetoothPanDevice panDevice = mPanDevices.get(device);
657         if (panDevice == null) {
658             Log.i(TAG, "state " + state + " Num of connected pan devices: " + mPanDevices.size());
659             prevState = BluetoothProfile.STATE_DISCONNECTED;
660             panDevice = new BluetoothPanDevice(state, iface, localRole, remoteRole);
661             mPanDevices.put(device, panDevice);
662         } else {
663             prevState = panDevice.mState;
664             panDevice.mState = state;
665             panDevice.mLocalRole = localRole;
666             panDevice.mRemoteRole = remoteRole;
667             panDevice.mIface = iface;
668         }
669 
670         // Avoid race condition that gets this class stuck in STATE_DISCONNECTING. While we
671         // are in STATE_CONNECTING, if a BluetoothPan#disconnect call comes in, the original
672         // connect call will put us in STATE_DISCONNECTED. Then, the disconnect completes and
673         // changes the state to STATE_DISCONNECTING. All future calls to BluetoothPan#connect
674         // will fail until the caller explicitly calls BluetoothPan#disconnect.
675         if (prevState == BluetoothProfile.STATE_DISCONNECTED
676                 && state == BluetoothProfile.STATE_DISCONNECTING) {
677             Log.d(TAG, "Ignoring state change from " + prevState + " to " + state);
678             mPanDevices.remove(device);
679             return;
680         }
681 
682         Log.d(TAG, "handlePanDeviceStateChange preState: " + prevState + " state: " + state);
683         if (prevState == state) {
684             return;
685         }
686         if (remoteRole == BluetoothPan.LOCAL_PANU_ROLE) {
687             if (state == BluetoothProfile.STATE_CONNECTED) {
688                 if ((!mTetherOn) || (localRole == BluetoothPan.LOCAL_PANU_ROLE)) {
689                     Log.d(TAG, "handlePanDeviceStateChange BT tethering is off/Local role"
690                             + " is PANU drop the connection");
691                     mPanDevices.remove(device);
692                     disconnectPanNative(Utils.getByteAddress(device));
693                     return;
694                 }
695                 Log.d(TAG, "handlePanDeviceStateChange LOCAL_NAP_ROLE:REMOTE_PANU_ROLE");
696                 if (!mIsTethering) {
697                     mIsTethering = true;
698                     try {
699                         for (IBluetoothPanCallback cb : mBluetoothTetheringCallbacks.values()) {
700                             cb.onAvailable(iface);
701                         }
702                     } catch (RemoteException e) {
703                         throw e.rethrowFromSystemServer();
704                     }
705                 }
706             } else if (state == BluetoothProfile.STATE_DISCONNECTED) {
707                 mPanDevices.remove(device);
708                 Log.i(TAG, "remote(PANU) is disconnected, Remaining connected PANU devices: "
709                         + mPanDevices.size());
710                 if (mIsTethering && mPanDevices.size() == 0) {
711                     try {
712                         for (IBluetoothPanCallback cb : mBluetoothTetheringCallbacks.values()) {
713                             cb.onUnavailable();
714                         }
715                     } catch (RemoteException e) {
716                         throw e.rethrowFromSystemServer();
717                     }
718                     mIsTethering = false;
719                 }
720             }
721         } else if (mStarted) {
722             // PANU Role = reverse Tether
723 
724             Log.d(TAG, "handlePanDeviceStateChange LOCAL_PANU_ROLE:REMOTE_NAP_ROLE state = " + state
725                     + ", prevState = " + prevState);
726             if (state == BluetoothProfile.STATE_CONNECTED) {
727                 mNetworkFactory = new BluetoothTetheringNetworkFactory(
728                         getBaseContext(), getMainLooper(), this);
729                 mNetworkFactory.startReverseTether(iface);
730             } else if (state == BluetoothProfile.STATE_DISCONNECTED) {
731                 if (mNetworkFactory != null) {
732                     mNetworkFactory.stopReverseTether();
733                     mNetworkFactory = null;
734                 }
735                 mPanDevices.remove(device);
736             }
737         }
738 
739         if (state == BluetoothProfile.STATE_CONNECTED) {
740             MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.PAN);
741         }
742         /* Notifying the connection state change of the profile before sending the intent for
743            connection state change, as it was causing a race condition, with the UI not being
744            updated with the correct connection state. */
745         Log.d(TAG, "Pan Device state : device: " + device + " State:" + prevState + "->" + state);
746         Intent intent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
747         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
748         intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_STATE, prevState);
749         intent.putExtra(BluetoothPan.EXTRA_STATE, state);
750         intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, localRole);
751         sendBroadcast(intent, BLUETOOTH_CONNECT);
752     }
753 
754     @Override
dump(StringBuilder sb)755     public void dump(StringBuilder sb) {
756         super.dump(sb);
757         println(sb, "mMaxPanDevices: " + mMaxPanDevices);
758         println(sb, "mPanIfName: " + mPanIfName);
759         println(sb, "mTetherOn: " + mTetherOn);
760         println(sb, "mPanDevices:");
761         for (BluetoothDevice device : mPanDevices.keySet()) {
762             println(sb, "  " + device + " : " + mPanDevices.get(device));
763         }
764     }
765 
766     @VisibleForTesting
767     static class BluetoothPanDevice {
768         private int mState;
769         private String mIface;
770         private int mLocalRole; // Which local role is this PAN device bound to
771         private int mRemoteRole; // Which remote role is this PAN device bound to
772 
BluetoothPanDevice(int state, String iface, int localRole, int remoteRole)773         BluetoothPanDevice(int state, String iface, int localRole, int remoteRole) {
774             mState = state;
775             mIface = iface;
776             mLocalRole = localRole;
777             mRemoteRole = remoteRole;
778         }
779     }
780 
781     // Constants matching Hal header file bt_hh.h
782     // bthh_connection_state_t
783     @VisibleForTesting
784     static final int CONN_STATE_CONNECTED = 0;
785     @VisibleForTesting
786     static final int CONN_STATE_CONNECTING = 1;
787     @VisibleForTesting
788     static final int CONN_STATE_DISCONNECTED = 2;
789     @VisibleForTesting
790     static final int CONN_STATE_DISCONNECTING = 3;
791 
classInitNative()792     private static native void classInitNative();
793 
initializeNative()794     private native void initializeNative();
795 
cleanupNative()796     private native void cleanupNative();
797 
connectPanNative(byte[] btAddress, int localRole, int remoteRole)798     private native boolean connectPanNative(byte[] btAddress, int localRole, int remoteRole);
799 
disconnectPanNative(byte[] btAddress)800     private native boolean disconnectPanNative(byte[] btAddress);
801 
802 }
803