1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.bluetooth;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.RequiresFeature;
22 import android.annotation.RequiresNoPermission;
23 import android.annotation.RequiresPermission;
24 import android.annotation.SystemService;
25 import android.app.ActivityThread;
26 import android.app.AppGlobals;
27 import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
28 import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
29 import android.content.Attributable;
30 import android.content.AttributionSource;
31 import android.content.Context;
32 import android.content.pm.PackageManager;
33 import android.os.RemoteException;
34 import android.util.Log;
35 
36 import java.util.ArrayList;
37 import java.util.List;
38 
39 /**
40  * High level manager used to obtain an instance of an {@link BluetoothAdapter}
41  * and to conduct overall Bluetooth Management.
42  * <p>
43  * Use {@link android.content.Context#getSystemService(java.lang.String)}
44  * with {@link Context#BLUETOOTH_SERVICE} to create an {@link BluetoothManager},
45  * then call {@link #getAdapter} to obtain the {@link BluetoothAdapter}.
46  * </p>
47  * <div class="special reference">
48  * <h3>Developer Guides</h3>
49  * <p>
50  * For more information about using BLUETOOTH, read the <a href=
51  * "{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer
52  * guide.
53  * </p>
54  * </div>
55  *
56  * @see Context#getSystemService
57  * @see BluetoothAdapter#getDefaultAdapter()
58  */
59 @SystemService(Context.BLUETOOTH_SERVICE)
60 @RequiresFeature(PackageManager.FEATURE_BLUETOOTH)
61 public final class BluetoothManager {
62     private static final String TAG = "BluetoothManager";
63     private static final boolean DBG = false;
64 
65     private final AttributionSource mAttributionSource;
66     private final BluetoothAdapter mAdapter;
67 
68     /**
69      * @hide
70      */
BluetoothManager(Context context)71     public BluetoothManager(Context context) {
72         mAttributionSource = resolveAttributionSource(context);
73         mAdapter = BluetoothAdapter.createAdapter(mAttributionSource);
74     }
75 
76     /** {@hide} */
resolveAttributionSource(@ullable Context context)77     public static @NonNull AttributionSource resolveAttributionSource(@Nullable Context context) {
78         AttributionSource res = null;
79         if (context != null) {
80             res = context.getAttributionSource();
81         }
82         if (res == null) {
83             res = ActivityThread.currentAttributionSource();
84         }
85         if (res == null) {
86             int uid = android.os.Process.myUid();
87             if (uid == android.os.Process.ROOT_UID) {
88                 uid = android.os.Process.SYSTEM_UID;
89             }
90             try {
91                 res = new AttributionSource(uid,
92                         AppGlobals.getPackageManager().getPackagesForUid(uid)[0], null);
93             } catch (RemoteException ignored) {
94             }
95         }
96         if (res == null) {
97             throw new IllegalStateException("Failed to resolve AttributionSource");
98         }
99         return res;
100     }
101 
102     /**
103      * Get the BLUETOOTH Adapter for this device.
104      *
105      * @return the BLUETOOTH Adapter
106      */
107     @RequiresNoPermission
getAdapter()108     public BluetoothAdapter getAdapter() {
109         return mAdapter;
110     }
111 
112     /**
113      * Get the current connection state of the profile to the remote device.
114      *
115      * <p>This is not specific to any application configuration but represents
116      * the connection state of the local Bluetooth adapter for certain profile.
117      * This can be used by applications like status bar which would just like
118      * to know the state of Bluetooth.
119      *
120      * @param device Remote bluetooth device.
121      * @param profile GATT or GATT_SERVER
122      * @return State of the profile connection. One of {@link BluetoothProfile#STATE_CONNECTED},
123      * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_DISCONNECTED},
124      * {@link BluetoothProfile#STATE_DISCONNECTING}
125      */
126     @RequiresLegacyBluetoothPermission
127     @RequiresBluetoothConnectPermission
128     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getConnectionState(BluetoothDevice device, int profile)129     public int getConnectionState(BluetoothDevice device, int profile) {
130         if (DBG) Log.d(TAG, "getConnectionState()");
131 
132         List<BluetoothDevice> connectedDevices = getConnectedDevices(profile);
133         for (BluetoothDevice connectedDevice : connectedDevices) {
134             if (device.equals(connectedDevice)) {
135                 return BluetoothProfile.STATE_CONNECTED;
136             }
137         }
138 
139         return BluetoothProfile.STATE_DISCONNECTED;
140     }
141 
142     /**
143      * Get connected devices for the specified profile.
144      *
145      * <p> Return the set of devices which are in state {@link BluetoothProfile#STATE_CONNECTED}
146      *
147      * <p>This is not specific to any application configuration but represents
148      * the connection state of Bluetooth for this profile.
149      * This can be used by applications like status bar which would just like
150      * to know the state of Bluetooth.
151      *
152      * @param profile GATT or GATT_SERVER
153      * @return List of devices. The list will be empty on error.
154      */
155     @RequiresLegacyBluetoothPermission
156     @RequiresBluetoothConnectPermission
157     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getConnectedDevices(int profile)158     public List<BluetoothDevice> getConnectedDevices(int profile) {
159         if (DBG) Log.d(TAG, "getConnectedDevices");
160         return getDevicesMatchingConnectionStates(profile, new int[] {
161                 BluetoothProfile.STATE_CONNECTED
162         });
163     }
164 
165     /**
166      * Get a list of devices that match any of the given connection
167      * states.
168      *
169      * <p> If none of the devices match any of the given states,
170      * an empty list will be returned.
171      *
172      * <p>This is not specific to any application configuration but represents
173      * the connection state of the local Bluetooth adapter for this profile.
174      * This can be used by applications like status bar which would just like
175      * to know the state of the local adapter.
176      *
177      * @param profile GATT or GATT_SERVER
178      * @param states Array of states. States can be one of {@link BluetoothProfile#STATE_CONNECTED},
179      * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_DISCONNECTED},
180      * {@link BluetoothProfile#STATE_DISCONNECTING},
181      * @return List of devices. The list will be empty on error.
182      */
183     @RequiresLegacyBluetoothPermission
184     @RequiresBluetoothConnectPermission
185     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getDevicesMatchingConnectionStates(int profile, int[] states)186     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int profile, int[] states) {
187         if (DBG) Log.d(TAG, "getDevicesMatchingConnectionStates");
188 
189         if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) {
190             throw new IllegalArgumentException("Profile not supported: " + profile);
191         }
192 
193         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
194 
195         try {
196             IBluetoothManager managerService = mAdapter.getBluetoothManager();
197             IBluetoothGatt iGatt = managerService.getBluetoothGatt();
198             if (iGatt == null) return devices;
199             devices = Attributable.setAttributionSource(
200                     iGatt.getDevicesMatchingConnectionStates(states, mAttributionSource),
201                     mAttributionSource);
202         } catch (RemoteException e) {
203             Log.e(TAG, "", e);
204         }
205 
206         return devices;
207     }
208 
209     /**
210      * Open a GATT Server
211      * The callback is used to deliver results to Caller, such as connection status as well
212      * as the results of any other GATT server operations.
213      * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
214      * to conduct GATT server operations.
215      *
216      * @param context App context
217      * @param callback GATT server callback handler that will receive asynchronous callbacks.
218      * @return BluetoothGattServer instance
219      */
220     @RequiresBluetoothConnectPermission
221     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
openGattServer(Context context, BluetoothGattServerCallback callback)222     public BluetoothGattServer openGattServer(Context context,
223             BluetoothGattServerCallback callback) {
224 
225         return (openGattServer(context, callback, BluetoothDevice.TRANSPORT_AUTO));
226     }
227 
228     /**
229      * Open a GATT Server
230      * The callback is used to deliver results to Caller, such as connection status as well
231      * as the results of any other GATT server operations.
232      * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
233      * to conduct GATT server operations.
234      *
235      * @param context App context
236      * @param callback GATT server callback handler that will receive asynchronous callbacks.
237      * @param eatt_support idicates if server should use eatt channel for notifications.
238      * @return BluetoothGattServer instance
239      * @hide
240      */
241     @RequiresBluetoothConnectPermission
242     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
openGattServer(Context context, BluetoothGattServerCallback callback, boolean eatt_support)243     public BluetoothGattServer openGattServer(Context context,
244             BluetoothGattServerCallback callback, boolean eatt_support) {
245         return (openGattServer(context, callback, BluetoothDevice.TRANSPORT_AUTO, eatt_support));
246     }
247 
248     /**
249      * Open a GATT Server
250      * The callback is used to deliver results to Caller, such as connection status as well
251      * as the results of any other GATT server operations.
252      * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
253      * to conduct GATT server operations.
254      *
255      * @param context App context
256      * @param callback GATT server callback handler that will receive asynchronous callbacks.
257      * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
258      * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
259      * BluetoothDevice#TRANSPORT_LE}
260      * @return BluetoothGattServer instance
261      * @hide
262      */
263     @RequiresBluetoothConnectPermission
264     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
openGattServer(Context context, BluetoothGattServerCallback callback, int transport)265     public BluetoothGattServer openGattServer(Context context,
266             BluetoothGattServerCallback callback, int transport) {
267         return (openGattServer(context, callback, transport, false));
268     }
269 
270     /**
271      * Open a GATT Server
272      * The callback is used to deliver results to Caller, such as connection status as well
273      * as the results of any other GATT server operations.
274      * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
275      * to conduct GATT server operations.
276      *
277      * @param context App context
278      * @param callback GATT server callback handler that will receive asynchronous callbacks.
279      * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
280      * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
281      * BluetoothDevice#TRANSPORT_LE}
282      * @param eatt_support idicates if server should use eatt channel for notifications.
283      * @return BluetoothGattServer instance
284      * @hide
285      */
286     @RequiresBluetoothConnectPermission
287     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
openGattServer(Context context, BluetoothGattServerCallback callback, int transport, boolean eatt_support)288     public BluetoothGattServer openGattServer(Context context,
289             BluetoothGattServerCallback callback, int transport, boolean eatt_support) {
290         if (context == null || callback == null) {
291             throw new IllegalArgumentException("null parameter: " + context + " " + callback);
292         }
293 
294         // TODO(Bluetooth) check whether platform support BLE
295         //     Do the check here or in GattServer?
296 
297         try {
298             IBluetoothManager managerService = mAdapter.getBluetoothManager();
299             IBluetoothGatt iGatt = managerService.getBluetoothGatt();
300             if (iGatt == null) {
301                 Log.e(TAG, "Fail to get GATT Server connection");
302                 return null;
303             }
304             BluetoothGattServer mGattServer =
305                     new BluetoothGattServer(iGatt, transport, mAdapter);
306             Boolean regStatus = mGattServer.registerCallback(callback, eatt_support);
307             return regStatus ? mGattServer : null;
308         } catch (RemoteException e) {
309             Log.e(TAG, "", e);
310             return null;
311         }
312     }
313 }
314