blob: cbc96c07333811acd3133ff3181acfdafe6faaa5 [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;
jovanak2a9131f2018-10-15 17:50:19 -070026import android.os.UserHandle;
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080027import android.util.Log;
28
Jack Hea355e5e2017-08-22 16:06:54 -070029import java.util.ArrayList;
30import java.util.List;
31
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080032/**
33 * This class provides the APIs to control the Bluetooth PBAP Client Profile.
Jack Hea355e5e2017-08-22 16:06:54 -070034 *
35 * @hide
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080036 */
37public final class BluetoothPbapClient implements BluetoothProfile {
38
39 private static final String TAG = "BluetoothPbapClient";
40 private static final boolean DBG = false;
41 private static final boolean VDBG = false;
42
43 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack He37ab8152017-10-02 19:08:30 -070044 "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED";
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080045
Jack He16eeac32017-08-17 12:11:18 -070046 private volatile IBluetoothPbapClient mService;
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080047 private final Context mContext;
48 private ServiceListener mServiceListener;
49 private BluetoothAdapter mAdapter;
50
51 /** There was an error trying to obtain the state */
Jack Hea355e5e2017-08-22 16:06:54 -070052 public static final int STATE_ERROR = -1;
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080053
54 public static final int RESULT_FAILURE = 0;
55 public static final int RESULT_SUCCESS = 1;
56 /** Connection canceled before completion. */
57 public static final int RESULT_CANCELED = 2;
58
Jack He2992cd02017-08-22 21:21:23 -070059 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080060 new IBluetoothStateChangeCallback.Stub() {
61 public void onBluetoothStateChange(boolean up) {
62 if (DBG) {
63 Log.d(TAG, "onBluetoothStateChange: PBAP CLIENT up=" + up);
64 }
65 if (!up) {
66 if (VDBG) {
Jack Hea355e5e2017-08-22 16:06:54 -070067 Log.d(TAG, "Unbinding service...");
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080068 }
69 synchronized (mConnection) {
70 try {
71 mService = null;
72 mContext.unbindService(mConnection);
73 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -070074 Log.e(TAG, "", re);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080075 }
76 }
77 } else {
78 synchronized (mConnection) {
79 try {
80 if (mService == null) {
81 if (VDBG) {
Jack Hea355e5e2017-08-22 16:06:54 -070082 Log.d(TAG, "Binding service...");
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080083 }
84 doBind();
85 }
86 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -070087 Log.e(TAG, "", re);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080088 }
89 }
90 }
91 }
Jack Hea355e5e2017-08-22 16:06:54 -070092 };
Joseph Pirozzocfa8a642016-03-04 13:02:54 -080093
94 /**
95 * Create a BluetoothPbapClient proxy object.
96 */
97 BluetoothPbapClient(Context context, ServiceListener l) {
98 if (DBG) {
99 Log.d(TAG, "Create BluetoothPbapClient proxy object");
100 }
101 mContext = context;
102 mServiceListener = l;
103 mAdapter = BluetoothAdapter.getDefaultAdapter();
104 IBluetoothManager mgr = mAdapter.getBluetoothManager();
105 if (mgr != null) {
106 try {
107 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
108 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700109 Log.e(TAG, "", e);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800110 }
111 }
112 doBind();
113 }
114
115 private boolean doBind() {
116 Intent intent = new Intent(IBluetoothPbapClient.class.getName());
117 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
118 intent.setComponent(comp);
119 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
jovanak2a9131f2018-10-15 17:50:19 -0700120 UserHandle.CURRENT_OR_SELF)) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800121 Log.e(TAG, "Could not bind to Bluetooth PBAP Client Service with " + intent);
122 return false;
123 }
124 return true;
125 }
126
127 protected void finalize() throws Throwable {
128 try {
129 close();
130 } finally {
131 super.finalize();
132 }
133 }
134
135 /**
136 * Close the connection to the backing service.
137 * Other public functions of BluetoothPbapClient will return default error
138 * results once close() has been called. Multiple invocations of close()
139 * are ok.
140 */
141 public synchronized void close() {
142 IBluetoothManager mgr = mAdapter.getBluetoothManager();
143 if (mgr != null) {
144 try {
145 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
146 } catch (Exception e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700147 Log.e(TAG, "", e);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800148 }
149 }
150
151 synchronized (mConnection) {
152 if (mService != null) {
153 try {
154 mService = null;
155 mContext.unbindService(mConnection);
156 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700157 Log.e(TAG, "", re);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800158 }
159 }
160 }
161 mServiceListener = null;
162 }
163
164 /**
165 * Initiate connection.
166 * Upon successful connection to remote PBAP server the Client will
167 * attempt to automatically download the users phonebook and call log.
168 *
Jack Hea355e5e2017-08-22 16:06:54 -0700169 * @param device a remote device we want connect to
170 * @return <code>true</code> if command has been issued successfully; <code>false</code>
171 * otherwise;
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800172 */
173 public boolean connect(BluetoothDevice device) {
174 if (DBG) {
175 log("connect(" + device + ") for PBAP Client.");
176 }
Jack He16eeac32017-08-17 12:11:18 -0700177 final IBluetoothPbapClient service = mService;
178 if (service != null && isEnabled() && isValidDevice(device)) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800179 try {
Jack He16eeac32017-08-17 12:11:18 -0700180 return service.connect(device);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800181 } catch (RemoteException e) {
182 Log.e(TAG, Log.getStackTraceString(new Throwable()));
183 return false;
184 }
185 }
Jack He16eeac32017-08-17 12:11:18 -0700186 if (service == null) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800187 Log.w(TAG, "Proxy not attached to service");
188 }
189 return false;
190 }
191
192 /**
193 * Initiate disconnect.
194 *
195 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700196 * @return false on error, true otherwise
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800197 */
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700198 public boolean disconnect(BluetoothDevice device) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800199 if (DBG) {
Jack Hea355e5e2017-08-22 16:06:54 -0700200 log("disconnect(" + device + ")" + new Exception());
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800201 }
Jack He16eeac32017-08-17 12:11:18 -0700202 final IBluetoothPbapClient service = mService;
203 if (service != null && isEnabled() && isValidDevice(device)) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800204 try {
Jack He16eeac32017-08-17 12:11:18 -0700205 service.disconnect(device);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800206 return true;
207 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700208 Log.e(TAG, Log.getStackTraceString(new Throwable()));
209 return false;
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800210 }
211 }
Jack He16eeac32017-08-17 12:11:18 -0700212 if (service == null) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800213 Log.w(TAG, "Proxy not attached to service");
214 }
215 return false;
216 }
217
218 /**
219 * Get the list of connected devices.
220 * Currently at most one.
221 *
222 * @return list of connected devices
223 */
224 @Override
225 public List<BluetoothDevice> getConnectedDevices() {
226 if (DBG) {
227 log("getConnectedDevices()");
228 }
Jack He16eeac32017-08-17 12:11:18 -0700229 final IBluetoothPbapClient service = mService;
230 if (service != null && isEnabled()) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800231 try {
Jack He16eeac32017-08-17 12:11:18 -0700232 return service.getConnectedDevices();
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800233 } catch (RemoteException e) {
234 Log.e(TAG, Log.getStackTraceString(new Throwable()));
235 return new ArrayList<BluetoothDevice>();
236 }
237 }
Jack He16eeac32017-08-17 12:11:18 -0700238 if (service == null) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800239 Log.w(TAG, "Proxy not attached to service");
240 }
241 return new ArrayList<BluetoothDevice>();
242 }
243
244 /**
245 * Get the list of devices matching specified states. Currently at most one.
246 *
247 * @return list of matching devices
248 */
249 @Override
250 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
251 if (DBG) {
252 log("getDevicesMatchingStates()");
253 }
Jack He16eeac32017-08-17 12:11:18 -0700254 final IBluetoothPbapClient service = mService;
255 if (service != null && isEnabled()) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800256 try {
Jack He16eeac32017-08-17 12:11:18 -0700257 return service.getDevicesMatchingConnectionStates(states);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800258 } catch (RemoteException e) {
259 Log.e(TAG, Log.getStackTraceString(new Throwable()));
260 return new ArrayList<BluetoothDevice>();
261 }
262 }
Jack He16eeac32017-08-17 12:11:18 -0700263 if (service == null) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800264 Log.w(TAG, "Proxy not attached to service");
265 }
266 return new ArrayList<BluetoothDevice>();
267 }
268
269 /**
270 * Get connection state of device
271 *
272 * @return device connection state
273 */
274 @Override
275 public int getConnectionState(BluetoothDevice device) {
276 if (DBG) {
277 log("getConnectionState(" + device + ")");
278 }
Jack He16eeac32017-08-17 12:11:18 -0700279 final IBluetoothPbapClient service = mService;
280 if (service != null && isEnabled() && isValidDevice(device)) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800281 try {
Jack He16eeac32017-08-17 12:11:18 -0700282 return service.getConnectionState(device);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800283 } catch (RemoteException e) {
284 Log.e(TAG, Log.getStackTraceString(new Throwable()));
285 return BluetoothProfile.STATE_DISCONNECTED;
286 }
287 }
Jack He16eeac32017-08-17 12:11:18 -0700288 if (service == null) {
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800289 Log.w(TAG, "Proxy not attached to service");
290 }
291 return BluetoothProfile.STATE_DISCONNECTED;
292 }
293
294 private final ServiceConnection mConnection = new ServiceConnection() {
295 public void onServiceConnected(ComponentName className, IBinder service) {
296 if (DBG) {
297 log("Proxy object connected");
298 }
Jeff Sharkey0a17db12016-11-04 11:23:46 -0600299 mService = IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service));
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800300 if (mServiceListener != null) {
Jack Hea355e5e2017-08-22 16:06:54 -0700301 mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT,
302 BluetoothPbapClient.this);
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800303 }
304 }
Jack Hea355e5e2017-08-22 16:06:54 -0700305
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800306 public void onServiceDisconnected(ComponentName className) {
307 if (DBG) {
308 log("Proxy object disconnected");
309 }
310 mService = null;
311 if (mServiceListener != null) {
312 mServiceListener.onServiceDisconnected(BluetoothProfile.PBAP_CLIENT);
313 }
314 }
315 };
316
317 private static void log(String msg) {
318 Log.d(TAG, msg);
319 }
320
321 private boolean isEnabled() {
322 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
323 if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) {
324 return true;
325 }
326 log("Bluetooth is Not enabled");
327 return false;
328 }
329
Jack He16eeac32017-08-17 12:11:18 -0700330 private static boolean isValidDevice(BluetoothDevice device) {
331 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800332 }
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700333
334 /**
335 * Set priority of the profile
336 *
337 * <p> The device should already be paired.
Jack Hea355e5e2017-08-22 16:06:54 -0700338 * Priority can be one of {@link #PRIORITY_ON} or
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700339 * {@link #PRIORITY_OFF},
340 *
341 * @param device Paired bluetooth device
Jack He16eeac32017-08-17 12:11:18 -0700342 * @param priority Priority of this profile
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700343 * @return true if priority is set, false on error
344 */
345 public boolean setPriority(BluetoothDevice device, int priority) {
346 if (DBG) {
347 log("setPriority(" + device + ", " + priority + ")");
348 }
Jack He16eeac32017-08-17 12:11:18 -0700349 final IBluetoothPbapClient service = mService;
350 if (service != null && isEnabled() && isValidDevice(device)) {
Jack He2992cd02017-08-22 21:21:23 -0700351 if (priority != BluetoothProfile.PRIORITY_OFF
352 && priority != BluetoothProfile.PRIORITY_ON) {
Jack Hea355e5e2017-08-22 16:06:54 -0700353 return false;
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700354 }
355 try {
Jack He16eeac32017-08-17 12:11:18 -0700356 return service.setPriority(device, priority);
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700357 } catch (RemoteException e) {
358 Log.e(TAG, Log.getStackTraceString(new Throwable()));
359 return false;
360 }
361 }
Jack He16eeac32017-08-17 12:11:18 -0700362 if (service == null) {
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700363 Log.w(TAG, "Proxy not attached to service");
364 }
365 return false;
366 }
367
368 /**
369 * Get the priority of the profile.
370 *
371 * <p> The priority can be any of:
372 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
373 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
374 *
375 * @param device Bluetooth device
376 * @return priority of the device
377 */
378 public int getPriority(BluetoothDevice device) {
379 if (VDBG) {
380 log("getPriority(" + device + ")");
381 }
Jack He16eeac32017-08-17 12:11:18 -0700382 final IBluetoothPbapClient service = mService;
383 if (service != null && isEnabled() && isValidDevice(device)) {
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700384 try {
Jack He16eeac32017-08-17 12:11:18 -0700385 return service.getPriority(device);
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700386 } catch (RemoteException e) {
387 Log.e(TAG, Log.getStackTraceString(new Throwable()));
388 return PRIORITY_OFF;
389 }
390 }
Jack He16eeac32017-08-17 12:11:18 -0700391 if (service == null) {
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700392 Log.w(TAG, "Proxy not attached to service");
393 }
394 return PRIORITY_OFF;
395 }
Joseph Pirozzocfa8a642016-03-04 13:02:54 -0800396}