blob: 1446adc8b9c3c020f1eb9ecb9bba286e6394804b [file] [log] [blame]
Joseph Pirozzocfa8a642016-03-04 13:02:54 -08001/*
2 * Copyright (C) 2016 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
17package android.bluetooth;
18
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080019import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.ServiceConnection;
Jeff Sharkey0a17db12016-11-04 11:23:46 -060023import android.os.Binder;
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080024import android.os.IBinder;
Jack Hea355e5e2017-08-22 16:06:54 -070025import android.os.RemoteException;
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080026import android.util.Log;
27
Jack Hea355e5e2017-08-22 16:06:54 -070028import java.util.ArrayList;
29import java.util.List;
30
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080031/**
32 * This class provides the APIs to control the Bluetooth PBAP Client Profile.
Jack Hea355e5e2017-08-22 16:06:54 -070033 *
34 * @hide
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080035 */
36public final class BluetoothPbapClient implements BluetoothProfile {
37
38 private static final String TAG = "BluetoothPbapClient";
39 private static final boolean DBG = false;
40 private static final boolean VDBG = false;
41
42 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack He37ab8152017-10-02 19:08:30 -070043 "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED";
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080044
Jack He16eeac32017-08-17 12:11:18 -070045 private volatile IBluetoothPbapClient mService;
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080046 private final Context mContext;
47 private ServiceListener mServiceListener;
48 private BluetoothAdapter mAdapter;
49
50 /** There was an error trying to obtain the state */
Jack Hea355e5e2017-08-22 16:06:54 -070051 public static final int STATE_ERROR = -1;
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080052
53 public static final int RESULT_FAILURE = 0;
54 public static final int RESULT_SUCCESS = 1;
55 /** Connection canceled before completion. */
56 public static final int RESULT_CANCELED = 2;
57
Jack He2992cd02017-08-22 21:21:23 -070058 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080059 new IBluetoothStateChangeCallback.Stub() {
60 public void onBluetoothStateChange(boolean up) {
61 if (DBG) {
62 Log.d(TAG, "onBluetoothStateChange: PBAP CLIENT up=" + up);
63 }
64 if (!up) {
65 if (VDBG) {
Jack Hea355e5e2017-08-22 16:06:54 -070066 Log.d(TAG, "Unbinding service...");
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080067 }
68 synchronized (mConnection) {
69 try {
70 mService = null;
71 mContext.unbindService(mConnection);
72 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -070073 Log.e(TAG, "", re);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080074 }
75 }
76 } else {
77 synchronized (mConnection) {
78 try {
79 if (mService == null) {
80 if (VDBG) {
Jack Hea355e5e2017-08-22 16:06:54 -070081 Log.d(TAG, "Binding service...");
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080082 }
83 doBind();
84 }
85 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -070086 Log.e(TAG, "", re);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080087 }
88 }
89 }
90 }
Jack Hea355e5e2017-08-22 16:06:54 -070091 };
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080092
93 /**
94 * Create a BluetoothPbapClient proxy object.
95 */
96 BluetoothPbapClient(Context context, ServiceListener l) {
97 if (DBG) {
98 Log.d(TAG, "Create BluetoothPbapClient proxy object");
99 }
100 mContext = context;
101 mServiceListener = l;
102 mAdapter = BluetoothAdapter.getDefaultAdapter();
103 IBluetoothManager mgr = mAdapter.getBluetoothManager();
104 if (mgr != null) {
105 try {
106 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
107 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700108 Log.e(TAG, "", e);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800109 }
110 }
111 doBind();
112 }
113
114 private boolean doBind() {
115 Intent intent = new Intent(IBluetoothPbapClient.class.getName());
116 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
117 intent.setComponent(comp);
118 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700119 mContext.getUser())) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800120 Log.e(TAG, "Could not bind to Bluetooth PBAP Client Service with " + intent);
121 return false;
122 }
123 return true;
124 }
125
126 protected void finalize() throws Throwable {
127 try {
128 close();
129 } finally {
130 super.finalize();
131 }
132 }
133
134 /**
135 * Close the connection to the backing service.
136 * Other public functions of BluetoothPbapClient will return default error
137 * results once close() has been called. Multiple invocations of close()
138 * are ok.
139 */
140 public synchronized void close() {
141 IBluetoothManager mgr = mAdapter.getBluetoothManager();
142 if (mgr != null) {
143 try {
144 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
145 } catch (Exception e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700146 Log.e(TAG, "", e);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800147 }
148 }
149
150 synchronized (mConnection) {
151 if (mService != null) {
152 try {
153 mService = null;
154 mContext.unbindService(mConnection);
155 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700156 Log.e(TAG, "", re);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800157 }
158 }
159 }
160 mServiceListener = null;
161 }
162
163 /**
164 * Initiate connection.
165 * Upon successful connection to remote PBAP server the Client will
166 * attempt to automatically download the users phonebook and call log.
167 *
Jack Hea355e5e2017-08-22 16:06:54 -0700168 * @param device a remote device we want connect to
169 * @return <code>true</code> if command has been issued successfully; <code>false</code>
170 * otherwise;
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800171 */
172 public boolean connect(BluetoothDevice device) {
173 if (DBG) {
174 log("connect(" + device + ") for PBAP Client.");
175 }
Jack He16eeac32017-08-17 12:11:18 -0700176 final IBluetoothPbapClient service = mService;
177 if (service != null && isEnabled() && isValidDevice(device)) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800178 try {
Jack He16eeac32017-08-17 12:11:18 -0700179 return service.connect(device);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800180 } catch (RemoteException e) {
181 Log.e(TAG, Log.getStackTraceString(new Throwable()));
182 return false;
183 }
184 }
Jack He16eeac32017-08-17 12:11:18 -0700185 if (service == null) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800186 Log.w(TAG, "Proxy not attached to service");
187 }
188 return false;
189 }
190
191 /**
192 * Initiate disconnect.
193 *
194 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700195 * @return false on error, true otherwise
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800196 */
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700197 public boolean disconnect(BluetoothDevice device) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800198 if (DBG) {
Jack Hea355e5e2017-08-22 16:06:54 -0700199 log("disconnect(" + device + ")" + new Exception());
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800200 }
Jack He16eeac32017-08-17 12:11:18 -0700201 final IBluetoothPbapClient service = mService;
202 if (service != null && isEnabled() && isValidDevice(device)) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800203 try {
Jack He16eeac32017-08-17 12:11:18 -0700204 service.disconnect(device);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800205 return true;
206 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700207 Log.e(TAG, Log.getStackTraceString(new Throwable()));
208 return false;
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800209 }
210 }
Jack He16eeac32017-08-17 12:11:18 -0700211 if (service == null) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800212 Log.w(TAG, "Proxy not attached to service");
213 }
214 return false;
215 }
216
217 /**
218 * Get the list of connected devices.
219 * Currently at most one.
220 *
221 * @return list of connected devices
222 */
223 @Override
224 public List<BluetoothDevice> getConnectedDevices() {
225 if (DBG) {
226 log("getConnectedDevices()");
227 }
Jack He16eeac32017-08-17 12:11:18 -0700228 final IBluetoothPbapClient service = mService;
229 if (service != null && isEnabled()) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800230 try {
Jack He16eeac32017-08-17 12:11:18 -0700231 return service.getConnectedDevices();
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800232 } catch (RemoteException e) {
233 Log.e(TAG, Log.getStackTraceString(new Throwable()));
234 return new ArrayList<BluetoothDevice>();
235 }
236 }
Jack He16eeac32017-08-17 12:11:18 -0700237 if (service == null) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800238 Log.w(TAG, "Proxy not attached to service");
239 }
240 return new ArrayList<BluetoothDevice>();
241 }
242
243 /**
244 * Get the list of devices matching specified states. Currently at most one.
245 *
246 * @return list of matching devices
247 */
248 @Override
249 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
250 if (DBG) {
251 log("getDevicesMatchingStates()");
252 }
Jack He16eeac32017-08-17 12:11:18 -0700253 final IBluetoothPbapClient service = mService;
254 if (service != null && isEnabled()) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800255 try {
Jack He16eeac32017-08-17 12:11:18 -0700256 return service.getDevicesMatchingConnectionStates(states);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800257 } catch (RemoteException e) {
258 Log.e(TAG, Log.getStackTraceString(new Throwable()));
259 return new ArrayList<BluetoothDevice>();
260 }
261 }
Jack He16eeac32017-08-17 12:11:18 -0700262 if (service == null) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800263 Log.w(TAG, "Proxy not attached to service");
264 }
265 return new ArrayList<BluetoothDevice>();
266 }
267
268 /**
269 * Get connection state of device
270 *
271 * @return device connection state
272 */
273 @Override
274 public int getConnectionState(BluetoothDevice device) {
275 if (DBG) {
276 log("getConnectionState(" + device + ")");
277 }
Jack He16eeac32017-08-17 12:11:18 -0700278 final IBluetoothPbapClient service = mService;
279 if (service != null && isEnabled() && isValidDevice(device)) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800280 try {
Jack He16eeac32017-08-17 12:11:18 -0700281 return service.getConnectionState(device);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800282 } catch (RemoteException e) {
283 Log.e(TAG, Log.getStackTraceString(new Throwable()));
284 return BluetoothProfile.STATE_DISCONNECTED;
285 }
286 }
Jack He16eeac32017-08-17 12:11:18 -0700287 if (service == null) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800288 Log.w(TAG, "Proxy not attached to service");
289 }
290 return BluetoothProfile.STATE_DISCONNECTED;
291 }
292
293 private final ServiceConnection mConnection = new ServiceConnection() {
294 public void onServiceConnected(ComponentName className, IBinder service) {
295 if (DBG) {
296 log("Proxy object connected");
297 }
Jeff Sharkey0a17db12016-11-04 11:23:46 -0600298 mService = IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service));
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800299 if (mServiceListener != null) {
Jack Hea355e5e2017-08-22 16:06:54 -0700300 mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT,
301 BluetoothPbapClient.this);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800302 }
303 }
Jack Hea355e5e2017-08-22 16:06:54 -0700304
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800305 public void onServiceDisconnected(ComponentName className) {
306 if (DBG) {
307 log("Proxy object disconnected");
308 }
309 mService = null;
310 if (mServiceListener != null) {
311 mServiceListener.onServiceDisconnected(BluetoothProfile.PBAP_CLIENT);
312 }
313 }
314 };
315
316 private static void log(String msg) {
317 Log.d(TAG, msg);
318 }
319
320 private boolean isEnabled() {
321 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
322 if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) {
323 return true;
324 }
325 log("Bluetooth is Not enabled");
326 return false;
327 }
328
Jack He16eeac32017-08-17 12:11:18 -0700329 private static boolean isValidDevice(BluetoothDevice device) {
330 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800331 }
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700332
333 /**
334 * Set priority of the profile
335 *
336 * <p> The device should already be paired.
Jack Hea355e5e2017-08-22 16:06:54 -0700337 * Priority can be one of {@link #PRIORITY_ON} or
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700338 * {@link #PRIORITY_OFF},
339 *
340 * @param device Paired bluetooth device
Jack He16eeac32017-08-17 12:11:18 -0700341 * @param priority Priority of this profile
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700342 * @return true if priority is set, false on error
343 */
344 public boolean setPriority(BluetoothDevice device, int priority) {
345 if (DBG) {
346 log("setPriority(" + device + ", " + priority + ")");
347 }
Jack He16eeac32017-08-17 12:11:18 -0700348 final IBluetoothPbapClient service = mService;
349 if (service != null && isEnabled() && isValidDevice(device)) {
Jack He2992cd02017-08-22 21:21:23 -0700350 if (priority != BluetoothProfile.PRIORITY_OFF
351 && priority != BluetoothProfile.PRIORITY_ON) {
Jack Hea355e5e2017-08-22 16:06:54 -0700352 return false;
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700353 }
354 try {
Jack He16eeac32017-08-17 12:11:18 -0700355 return service.setPriority(device, priority);
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700356 } catch (RemoteException e) {
357 Log.e(TAG, Log.getStackTraceString(new Throwable()));
358 return false;
359 }
360 }
Jack He16eeac32017-08-17 12:11:18 -0700361 if (service == null) {
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700362 Log.w(TAG, "Proxy not attached to service");
363 }
364 return false;
365 }
366
367 /**
368 * Get the priority of the profile.
369 *
370 * <p> The priority can be any of:
371 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
372 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
373 *
374 * @param device Bluetooth device
375 * @return priority of the device
376 */
377 public int getPriority(BluetoothDevice device) {
378 if (VDBG) {
379 log("getPriority(" + device + ")");
380 }
Jack He16eeac32017-08-17 12:11:18 -0700381 final IBluetoothPbapClient service = mService;
382 if (service != null && isEnabled() && isValidDevice(device)) {
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700383 try {
Jack He16eeac32017-08-17 12:11:18 -0700384 return service.getPriority(device);
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700385 } catch (RemoteException e) {
386 Log.e(TAG, Log.getStackTraceString(new Throwable()));
387 return PRIORITY_OFF;
388 }
389 }
Jack He16eeac32017-08-17 12:11:18 -0700390 if (service == null) {
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700391 Log.w(TAG, "Proxy not attached to service");
392 }
393 return PRIORITY_OFF;
394 }
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800395}