blob: ae264e19bb7c2af58d575a60881e94faf4d4580d [file] [log] [blame]
Jiafa Liu3f416732009-07-02 16:36:02 +08001/*
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
17package android.bluetooth;
18
Jack He37ab8152017-10-02 19:08:30 -070019import android.annotation.SdkConstant;
Mathew Inwood4dc66d32018-08-01 15:07:20 +010020import android.annotation.UnsupportedAppUsage;
Jiafa Liu3f416732009-07-02 16:36:02 +080021import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.ServiceConnection;
Jiafa Liu3f416732009-07-02 16:36:02 +080025import android.os.IBinder;
Jack Hea355e5e2017-08-22 16:06:54 -070026import android.os.RemoteException;
Jiafa Liu3f416732009-07-02 16:36:02 +080027import android.util.Log;
28
Hansong Zhangfef6d812017-12-08 16:05:55 -080029import java.util.ArrayList;
30import java.util.Arrays;
31import java.util.List;
32
Jiafa Liu3f416732009-07-02 16:36:02 +080033/**
34 * The Android Bluetooth API is not finalized, and *will* change. Use at your
35 * own risk.
36 *
37 * Public API for controlling the Bluetooth Pbap Service. This includes
38 * Bluetooth Phone book Access profile.
39 * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap
40 * Service via IPC.
41 *
42 * Creating a BluetoothPbap object will create a binding with the
43 * BluetoothPbap service. Users of this object should call close() when they
44 * are finished with the BluetoothPbap, so that this proxy object can unbind
45 * from the service.
46 *
47 * This BluetoothPbap object is not immediately bound to the
48 * BluetoothPbap service. Use the ServiceListener interface to obtain a
49 * notification when it is bound, this is especially important if you wish to
50 * immediately call methods on BluetoothPbap after construction.
51 *
52 * Android only supports one connected Bluetooth Pce at a time.
53 *
54 * @hide
55 */
Hansong Zhangfef6d812017-12-08 16:05:55 -080056public class BluetoothPbap implements BluetoothProfile {
Jiafa Liu3f416732009-07-02 16:36:02 +080057
58 private static final String TAG = "BluetoothPbap";
Hansong Zhangfef6d812017-12-08 16:05:55 -080059 private static final boolean DBG = false;
Jiafa Liu3f416732009-07-02 16:36:02 +080060
Jack Hea355e5e2017-08-22 16:06:54 -070061 /**
Jack He37ab8152017-10-02 19:08:30 -070062 * Intent used to broadcast the change in connection state of the PBAP
63 * profile.
64 *
65 * <p>This intent will have 3 extras:
66 * <ul>
67 * <li> {@link BluetoothProfile#EXTRA_STATE} - The current state of the profile. </li>
68 * <li> {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
69 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
70 * </ul>
71 * <p>{@link BluetoothProfile#EXTRA_STATE} or {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}
72 * can be any of {@link BluetoothProfile#STATE_DISCONNECTED},
73 * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED},
74 * {@link BluetoothProfile#STATE_DISCONNECTING}.
75 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
76 * receive.
Jiafa Liu3f416732009-07-02 16:36:02 +080077 */
Jack He37ab8152017-10-02 19:08:30 -070078 @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
79 public static final String ACTION_CONNECTION_STATE_CHANGED =
80 "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
Jiafa Liu3f416732009-07-02 16:36:02 +080081
Jack He16eeac32017-08-17 12:11:18 -070082 private volatile IBluetoothPbap mService;
Jiafa Liu3f416732009-07-02 16:36:02 +080083 private final Context mContext;
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -080084 private ServiceListener mServiceListener;
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -070085 private BluetoothAdapter mAdapter;
Jiafa Liu3f416732009-07-02 16:36:02 +080086
Jiafa Liu3f416732009-07-02 16:36:02 +080087 public static final int RESULT_FAILURE = 0;
88 public static final int RESULT_SUCCESS = 1;
89 /** Connection canceled before completion. */
90 public static final int RESULT_CANCELED = 2;
91
Jiafa Liu3f416732009-07-02 16:36:02 +080092 /**
93 * An interface for notifying Bluetooth PCE IPC clients when they have
Jackson Fanc8b04a92009-07-28 12:15:49 +080094 * been connected to the BluetoothPbap service.
Jiafa Liu3f416732009-07-02 16:36:02 +080095 */
96 public interface ServiceListener {
97 /**
98 * Called to notify the client when this proxy object has been
99 * connected to the BluetoothPbap service. Clients must wait for
100 * this callback before making IPC calls on the BluetoothPbap
101 * service.
102 */
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700103 public void onServiceConnected(BluetoothPbap proxy);
Jiafa Liu3f416732009-07-02 16:36:02 +0800104
105 /**
106 * Called to notify the client that this proxy object has been
107 * disconnected from the BluetoothPbap service. Clients must not
108 * make IPC calls on the BluetoothPbap service after this callback.
109 * This callback will currently only occur if the application hosting
110 * the BluetoothPbap service, but may be called more often in future.
111 */
112 public void onServiceDisconnected();
113 }
114
Jack He2992cd02017-08-22 21:21:23 -0700115 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700116 new IBluetoothStateChangeCallback.Stub() {
117 public void onBluetoothStateChange(boolean up) {
Hansong Zhangfef6d812017-12-08 16:05:55 -0800118 log("onBluetoothStateChange: up=" + up);
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700119 if (!up) {
Hansong Zhangfef6d812017-12-08 16:05:55 -0800120 log("Unbinding service...");
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700121 synchronized (mConnection) {
122 try {
123 mService = null;
124 mContext.unbindService(mConnection);
125 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700126 Log.e(TAG, "", re);
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700127 }
128 }
129 } else {
130 synchronized (mConnection) {
131 try {
132 if (mService == null) {
Hansong Zhangfef6d812017-12-08 16:05:55 -0800133 log("Binding service...");
Dianne Hackborn221ea892013-08-04 16:50:16 -0700134 doBind();
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700135 }
136 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700137 Log.e(TAG, "", re);
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700138 }
139 }
140 }
141 }
Jack Hea355e5e2017-08-22 16:06:54 -0700142 };
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700143
Jiafa Liu3f416732009-07-02 16:36:02 +0800144 /**
Jackson Fanc8b04a92009-07-28 12:15:49 +0800145 * Create a BluetoothPbap proxy object.
Jiafa Liu3f416732009-07-02 16:36:02 +0800146 */
147 public BluetoothPbap(Context context, ServiceListener l) {
148 mContext = context;
149 mServiceListener = l;
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700150 mAdapter = BluetoothAdapter.getDefaultAdapter();
151 IBluetoothManager mgr = mAdapter.getBluetoothManager();
152 if (mgr != null) {
153 try {
154 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
155 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700156 Log.e(TAG, "", e);
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700157 }
158 }
Dianne Hackborn221ea892013-08-04 16:50:16 -0700159 doBind();
160 }
161
162 boolean doBind() {
163 Intent intent = new Intent(IBluetoothPbap.class.getName());
164 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
165 intent.setComponent(comp);
Dianne Hackborn466ce962014-03-19 18:06:58 -0700166 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700167 mContext.getUser())) {
Dianne Hackborn221ea892013-08-04 16:50:16 -0700168 Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
169 return false;
Jiafa Liu3f416732009-07-02 16:36:02 +0800170 }
Dianne Hackborn221ea892013-08-04 16:50:16 -0700171 return true;
Jiafa Liu3f416732009-07-02 16:36:02 +0800172 }
173
174 protected void finalize() throws Throwable {
175 try {
176 close();
177 } finally {
178 super.finalize();
179 }
180 }
181
182 /**
183 * Close the connection to the backing service.
Jackson Fanc8b04a92009-07-28 12:15:49 +0800184 * Other public functions of BluetoothPbap will return default error
Jiafa Liu3f416732009-07-02 16:36:02 +0800185 * results once close() has been called. Multiple invocations of close()
186 * are ok.
187 */
188 public synchronized void close() {
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700189 IBluetoothManager mgr = mAdapter.getBluetoothManager();
190 if (mgr != null) {
191 try {
192 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
193 } catch (Exception e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700194 Log.e(TAG, "", e);
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700195 }
196 }
197
198 synchronized (mConnection) {
199 if (mService != null) {
200 try {
201 mService = null;
202 mContext.unbindService(mConnection);
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700203 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700204 Log.e(TAG, "", re);
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700205 }
206 }
Jiafa Liu3f416732009-07-02 16:36:02 +0800207 }
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800208 mServiceListener = null;
Jiafa Liu3f416732009-07-02 16:36:02 +0800209 }
210
211 /**
Hansong Zhangfef6d812017-12-08 16:05:55 -0800212 * {@inheritDoc}
Jiafa Liu3f416732009-07-02 16:36:02 +0800213 */
Hansong Zhangfef6d812017-12-08 16:05:55 -0800214 @Override
215 public List<BluetoothDevice> getConnectedDevices() {
216 log("getConnectedDevices()");
Jack He16eeac32017-08-17 12:11:18 -0700217 final IBluetoothPbap service = mService;
Hansong Zhangfef6d812017-12-08 16:05:55 -0800218 if (service == null) {
Jiafa Liu3f416732009-07-02 16:36:02 +0800219 Log.w(TAG, "Proxy not attached to service");
Hansong Zhangfef6d812017-12-08 16:05:55 -0800220 return new ArrayList<BluetoothDevice>();
221 }
222 try {
223 return service.getConnectedDevices();
224 } catch (RemoteException e) {
225 Log.e(TAG, e.toString());
226 }
227 return new ArrayList<BluetoothDevice>();
228 }
229
230 /**
231 * {@inheritDoc}
232 */
233 @Override
234 public int getConnectionState(BluetoothDevice device) {
235 log("getConnectionState: device=" + device);
236 final IBluetoothPbap service = mService;
237 if (service == null) {
238 Log.w(TAG, "Proxy not attached to service");
239 return BluetoothProfile.STATE_DISCONNECTED;
240 }
241 try {
242 return service.getConnectionState(device);
243 } catch (RemoteException e) {
244 Log.e(TAG, e.toString());
Jiafa Liu3f416732009-07-02 16:36:02 +0800245 }
Jack He37ab8152017-10-02 19:08:30 -0700246 return BluetoothProfile.STATE_DISCONNECTED;
Jiafa Liu3f416732009-07-02 16:36:02 +0800247 }
248
249 /**
Hansong Zhangfef6d812017-12-08 16:05:55 -0800250 * {@inheritDoc}
Jiafa Liu3f416732009-07-02 16:36:02 +0800251 */
Hansong Zhangfef6d812017-12-08 16:05:55 -0800252 @Override
253 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
254 log("getDevicesMatchingConnectionStates: states=" + Arrays.toString(states));
Jack He16eeac32017-08-17 12:11:18 -0700255 final IBluetoothPbap service = mService;
Hansong Zhangfef6d812017-12-08 16:05:55 -0800256 if (service == null) {
Jiafa Liu3f416732009-07-02 16:36:02 +0800257 Log.w(TAG, "Proxy not attached to service");
Hansong Zhangfef6d812017-12-08 16:05:55 -0800258 return new ArrayList<BluetoothDevice>();
Jiafa Liu3f416732009-07-02 16:36:02 +0800259 }
Hansong Zhangfef6d812017-12-08 16:05:55 -0800260 try {
261 return service.getDevicesMatchingConnectionStates(states);
262 } catch (RemoteException e) {
263 Log.e(TAG, e.toString());
264 }
265 return new ArrayList<BluetoothDevice>();
Jiafa Liu3f416732009-07-02 16:36:02 +0800266 }
267
268 /**
Nick Pellybd022f42009-08-14 18:33:38 -0700269 * Returns true if the specified Bluetooth device is connected (does not
270 * include connecting). Returns false if not connected, or if this proxy
271 * object is not currently connected to the Pbap service.
Jiafa Liu3f416732009-07-02 16:36:02 +0800272 */
Hansong Zhangfef6d812017-12-08 16:05:55 -0800273 // TODO: This is currently being used by SettingsLib and internal app.
Nick Pellybd022f42009-08-14 18:33:38 -0700274 public boolean isConnected(BluetoothDevice device) {
Hansong Zhangfef6d812017-12-08 16:05:55 -0800275 return getConnectionState(device) == BluetoothAdapter.STATE_CONNECTED;
Jiafa Liu3f416732009-07-02 16:36:02 +0800276 }
277
278 /**
Nick Pellybd022f42009-08-14 18:33:38 -0700279 * Disconnects the current Pbap client (PCE). Currently this call blocks,
Jake Hambyf51eada2010-09-21 13:39:53 -0700280 * it may soon be made asynchronous. Returns false if this proxy object is
Jackson Fanc8b04a92009-07-28 12:15:49 +0800281 * not currently connected to the Pbap service.
Jiafa Liu3f416732009-07-02 16:36:02 +0800282 */
Hansong Zhangfef6d812017-12-08 16:05:55 -0800283 // TODO: This is currently being used by SettingsLib and will be used in the future.
284 // TODO: Must specify target device. Implement this in the service.
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100285 @UnsupportedAppUsage
Hansong Zhangfef6d812017-12-08 16:05:55 -0800286 public boolean disconnect(BluetoothDevice device) {
287 log("disconnect()");
Jack He16eeac32017-08-17 12:11:18 -0700288 final IBluetoothPbap service = mService;
Hansong Zhangfef6d812017-12-08 16:05:55 -0800289 if (service == null) {
Jiafa Liu3f416732009-07-02 16:36:02 +0800290 Log.w(TAG, "Proxy not attached to service");
Hansong Zhangfef6d812017-12-08 16:05:55 -0800291 return false;
292 }
293 try {
294 service.disconnect(device);
295 return true;
296 } catch (RemoteException e) {
297 Log.e(TAG, e.toString());
Jiafa Liu3f416732009-07-02 16:36:02 +0800298 }
299 return false;
300 }
301
Matthew Xie9b693992013-10-10 11:21:40 -0700302 private final ServiceConnection mConnection = new ServiceConnection() {
Jiafa Liu3f416732009-07-02 16:36:02 +0800303 public void onServiceConnected(ComponentName className, IBinder service) {
Hansong Zhangfef6d812017-12-08 16:05:55 -0800304 log("Proxy object connected");
Jiafa Liu3f416732009-07-02 16:36:02 +0800305 mService = IBluetoothPbap.Stub.asInterface(service);
306 if (mServiceListener != null) {
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700307 mServiceListener.onServiceConnected(BluetoothPbap.this);
Jiafa Liu3f416732009-07-02 16:36:02 +0800308 }
309 }
Jack Hea355e5e2017-08-22 16:06:54 -0700310
Jiafa Liu3f416732009-07-02 16:36:02 +0800311 public void onServiceDisconnected(ComponentName className) {
Hansong Zhangfef6d812017-12-08 16:05:55 -0800312 log("Proxy object disconnected");
Jiafa Liu3f416732009-07-02 16:36:02 +0800313 mService = null;
314 if (mServiceListener != null) {
315 mServiceListener.onServiceDisconnected();
316 }
317 }
318 };
319
320 private static void log(String msg) {
Hansong Zhangfef6d812017-12-08 16:05:55 -0800321 if (DBG) {
322 Log.d(TAG, msg);
323 }
Jiafa Liu3f416732009-07-02 16:36:02 +0800324 }
325}