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