• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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.Manifest;
20 import android.annotation.NonNull;
21 import android.annotation.RequiresPermission;
22 import android.annotation.SdkConstant;
23 import android.annotation.SuppressLint;
24 import android.annotation.SystemApi;
25 import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
26 import android.compat.annotation.UnsupportedAppUsage;
27 import android.content.AttributionSource;
28 import android.content.Context;
29 import android.os.Build;
30 import android.os.IBinder;
31 import android.os.RemoteException;
32 import android.util.Log;
33 
34 import java.util.Arrays;
35 import java.util.Collections;
36 import java.util.List;
37 
38 /**
39  * Public API for controlling the Bluetooth Pbap Service. This includes Bluetooth Phone book Access
40  * profile. BluetoothPbap is a proxy object for controlling the Bluetooth Pbap Service via IPC.
41  *
42  * <p>Creating a BluetoothPbap object will create a binding with the BluetoothPbap service. Users of
43  * this object should call close() when they are finished with the BluetoothPbap, so that this proxy
44  * object can unbind from the service.
45  *
46  * <p>This BluetoothPbap object is not immediately bound to the BluetoothPbap service. Use the
47  * ServiceListener interface to obtain a notification when it is bound, this is especially important
48  * if you wish to immediately call methods on BluetoothPbap after construction.
49  *
50  * <p>To get an instance of the BluetoothPbap class, you can call {@link
51  * BluetoothAdapter#getProfileProxy(Context, ServiceListener, int)} with the final param being
52  * {@link BluetoothProfile#PBAP}. The ServiceListener should be able to get the instance of
53  * BluetoothPbap in {@link android.bluetooth.BluetoothProfile.ServiceListener#onServiceConnected}.
54  *
55  * <p>Android only supports one connected Bluetooth Pce at a time.
56  *
57  * @hide
58  */
59 @SystemApi
60 public class BluetoothPbap implements BluetoothProfile {
61 
62     private static final String TAG = "BluetoothPbap";
63     private static final boolean DBG = false;
64 
65     /**
66      * Intent used to broadcast the change in connection state of the PBAP profile.
67      *
68      * <p>This intent will have 3 extras:
69      *
70      * <ul>
71      *   <li>{@link BluetoothProfile#EXTRA_STATE} - The current state of the profile.
72      *   <li>{@link BluetoothProfile#EXTRA_PREVIOUS_STATE}- The previous state of the profile.
73      *   <li>{@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
74      * </ul>
75      *
76      * <p>{@link BluetoothProfile#EXTRA_STATE} or {@link BluetoothProfile#EXTRA_PREVIOUS_STATE} can
77      * be any of {@link BluetoothProfile#STATE_DISCONNECTED}, {@link
78      * BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED}, {@link
79      * BluetoothProfile#STATE_DISCONNECTING}.
80      *
81      * @hide
82      */
83     @SuppressLint("ActionValue")
84     @SystemApi
85     @RequiresBluetoothConnectPermission
86     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
87     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
88     public static final String ACTION_CONNECTION_STATE_CHANGED =
89             "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
90 
91     private final AttributionSource mAttributionSource;
92 
93     /** @hide */
94     public static final int RESULT_FAILURE = 0;
95 
96     /** @hide */
97     public static final int RESULT_SUCCESS = 1;
98 
99     /**
100      * Connection canceled before completion.
101      *
102      * @hide
103      */
104     public static final int RESULT_CANCELED = 2;
105 
106     private BluetoothAdapter mAdapter;
107 
108     private IBluetoothPbap mService;
109 
110     /**
111      * Create a BluetoothPbap proxy object.
112      *
113      * @hide
114      */
BluetoothPbap(Context context, BluetoothAdapter adapter)115     public BluetoothPbap(Context context, BluetoothAdapter adapter) {
116         mAdapter = adapter;
117         mAttributionSource = adapter.getAttributionSource();
118         mService = null;
119     }
120 
121     /** @hide */
122     @Override
onServiceConnected(IBinder service)123     public void onServiceConnected(IBinder service) {
124         mService = IBluetoothPbap.Stub.asInterface(service);
125     }
126 
127     /** @hide */
128     @Override
onServiceDisconnected()129     public void onServiceDisconnected() {
130         mService = null;
131     }
132 
getService()133     private IBluetoothPbap getService() {
134         return mService;
135     }
136 
137     /** @hide */
138     @Override
getAdapter()139     public BluetoothAdapter getAdapter() {
140         return mAdapter;
141     }
142 
143     /**
144      * {@inheritDoc}
145      *
146      * @hide
147      */
148     @Override
149     @RequiresBluetoothConnectPermission
150     @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
getConnectedDevices()151     public List<BluetoothDevice> getConnectedDevices() {
152         log("getConnectedDevices()");
153         final IBluetoothPbap service = getService();
154         if (service == null) {
155             Log.w(TAG, "Proxy not attached to service");
156             return Collections.emptyList();
157         }
158         try {
159             return Attributable.setAttributionSource(
160                     service.getConnectedDevices(mAttributionSource), mAttributionSource);
161         } catch (RemoteException e) {
162             Log.e(TAG, e.toString());
163         }
164         return Collections.emptyList();
165     }
166 
167     /**
168      * {@inheritDoc}
169      *
170      * @hide
171      */
172     @SystemApi
173     @Override
174     @RequiresBluetoothConnectPermission
175     @RequiresPermission(
176             allOf = {
177                 android.Manifest.permission.BLUETOOTH_CONNECT,
178                 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
179             })
getConnectionState(@onNull BluetoothDevice device)180     public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) {
181         log("getConnectionState: device=" + device);
182         try {
183             final IBluetoothPbap service = getService();
184             if (service != null && isEnabled() && isValidDevice(device)) {
185                 return service.getConnectionState(device, mAttributionSource);
186             }
187             if (service == null) {
188                 Log.w(TAG, "Proxy not attached to service");
189             }
190             return BluetoothProfile.STATE_DISCONNECTED;
191         } catch (RemoteException e) {
192             Log.e(TAG, e.toString());
193         }
194         return BluetoothProfile.STATE_DISCONNECTED;
195     }
196 
197     /**
198      * {@inheritDoc}
199      *
200      * @hide
201      */
202     @Override
203     @RequiresBluetoothConnectPermission
204     @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
getDevicesMatchingConnectionStates(int[] states)205     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
206         log("getDevicesMatchingConnectionStates: states=" + Arrays.toString(states));
207         final IBluetoothPbap service = getService();
208         if (service == null) {
209             Log.w(TAG, "Proxy not attached to service");
210             return Collections.emptyList();
211         }
212         try {
213             return Attributable.setAttributionSource(
214                     service.getDevicesMatchingConnectionStates(states, mAttributionSource),
215                     mAttributionSource);
216         } catch (RemoteException e) {
217             Log.e(TAG, e.toString());
218         }
219         return Collections.emptyList();
220     }
221 
222     /**
223      * Set connection policy of the profile and tries to disconnect it if connectionPolicy is {@link
224      * BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}
225      *
226      * <p>The device should already be paired. Connection policy can be one of: {@link
227      * BluetoothProfile#CONNECTION_POLICY_ALLOWED}, {@link
228      * BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link
229      * BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
230      *
231      * @param device Paired bluetooth device
232      * @param connectionPolicy is the connection policy to set to for this profile
233      * @return true if connectionPolicy is set, false on error
234      * @hide
235      */
236     @SystemApi
237     @RequiresBluetoothConnectPermission
238     @RequiresPermission(
239             allOf = {
240                 android.Manifest.permission.BLUETOOTH_CONNECT,
241                 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
242             })
setConnectionPolicy( @onNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy)243     public boolean setConnectionPolicy(
244             @NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) {
245         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
246         try {
247             final IBluetoothPbap service = getService();
248             if (service != null && isEnabled() && isValidDevice(device)) {
249                 if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
250                         && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
251                     return false;
252                 }
253                 return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
254             }
255             if (service == null) Log.w(TAG, "Proxy not attached to service");
256             return false;
257         } catch (RemoteException e) {
258             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
259             return false;
260         }
261     }
262 
263     /**
264      * Disconnects the current Pbap client (PCE). Currently this call blocks, it may soon be made
265      * asynchronous. Returns false if this proxy object is not currently connected to the Pbap
266      * service.
267      *
268      * @hide
269      */
270     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
271     @RequiresBluetoothConnectPermission
272     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
disconnect(BluetoothDevice device)273     public boolean disconnect(BluetoothDevice device) {
274         log("disconnect()");
275         final IBluetoothPbap service = getService();
276         if (service == null) {
277             Log.w(TAG, "Proxy not attached to service");
278             return false;
279         }
280         try {
281             service.disconnect(device, mAttributionSource);
282             return true;
283         } catch (RemoteException e) {
284             Log.e(TAG, e.toString());
285         }
286         return false;
287     }
288 
isEnabled()289     private boolean isEnabled() {
290         if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
291         return false;
292     }
293 
isValidDevice(BluetoothDevice device)294     private boolean isValidDevice(BluetoothDevice device) {
295         if (device == null) return false;
296 
297         if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
298         return false;
299     }
300 
log(String msg)301     private static void log(String msg) {
302         if (DBG) {
303             Log.d(TAG, msg);
304         }
305     }
306 }
307