blob: a601df02d0323160d5570c023d8f9e79d3e06739 [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 Inwood7acad5e2018-08-01 15:00:35 +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;
jovanak2a9131f2018-10-15 17:50:19 -070027import android.os.UserHandle;
Jiafa Liu3f416732009-07-02 16:36:02 +080028import android.util.Log;
29
Hansong Zhangfef6d812017-12-08 16:05:55 -080030import java.util.ArrayList;
31import java.util.Arrays;
32import java.util.List;
33
Jiafa Liu3f416732009-07-02 16:36:02 +080034/**
35 * The Android Bluetooth API is not finalized, and *will* change. Use at your
36 * own risk.
37 *
38 * Public API for controlling the Bluetooth Pbap Service. This includes
39 * Bluetooth Phone book Access profile.
40 * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap
41 * Service via IPC.
42 *
43 * Creating a BluetoothPbap object will create a binding with the
44 * BluetoothPbap service. Users of this object should call close() when they
45 * are finished with the BluetoothPbap, so that this proxy object can unbind
46 * from the service.
47 *
48 * This BluetoothPbap object is not immediately bound to the
49 * BluetoothPbap service. Use the ServiceListener interface to obtain a
50 * notification when it is bound, this is especially important if you wish to
51 * immediately call methods on BluetoothPbap after construction.
52 *
53 * Android only supports one connected Bluetooth Pce at a time.
54 *
55 * @hide
56 */
Hansong Zhangfef6d812017-12-08 16:05:55 -080057public class BluetoothPbap implements BluetoothProfile {
Jiafa Liu3f416732009-07-02 16:36:02 +080058
59 private static final String TAG = "BluetoothPbap";
Hansong Zhangfef6d812017-12-08 16:05:55 -080060 private static final boolean DBG = false;
Jiafa Liu3f416732009-07-02 16:36:02 +080061
Jack Hea355e5e2017-08-22 16:06:54 -070062 /**
Jack He37ab8152017-10-02 19:08:30 -070063 * Intent used to broadcast the change in connection state of the PBAP
64 * profile.
65 *
66 * <p>This intent will have 3 extras:
67 * <ul>
68 * <li> {@link BluetoothProfile#EXTRA_STATE} - The current state of the profile. </li>
69 * <li> {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
70 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
71 * </ul>
72 * <p>{@link BluetoothProfile#EXTRA_STATE} or {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}
73 * can be any of {@link BluetoothProfile#STATE_DISCONNECTED},
74 * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED},
75 * {@link BluetoothProfile#STATE_DISCONNECTING}.
76 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
77 * receive.
Jiafa Liu3f416732009-07-02 16:36:02 +080078 */
Jack He37ab8152017-10-02 19:08:30 -070079 @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
80 public static final String ACTION_CONNECTION_STATE_CHANGED =
81 "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
Jiafa Liu3f416732009-07-02 16:36:02 +080082
Jack He16eeac32017-08-17 12:11:18 -070083 private volatile IBluetoothPbap mService;
Jiafa Liu3f416732009-07-02 16:36:02 +080084 private final Context mContext;
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -080085 private ServiceListener mServiceListener;
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -070086 private BluetoothAdapter mAdapter;
Jiafa Liu3f416732009-07-02 16:36:02 +080087
Jiafa Liu3f416732009-07-02 16:36:02 +080088 public static final int RESULT_FAILURE = 0;
89 public static final int RESULT_SUCCESS = 1;
90 /** Connection canceled before completion. */
91 public static final int RESULT_CANCELED = 2;
92
Jiafa Liu3f416732009-07-02 16:36:02 +080093 /**
94 * An interface for notifying Bluetooth PCE IPC clients when they have
Jackson Fanc8b04a92009-07-28 12:15:49 +080095 * been connected to the BluetoothPbap service.
Jiafa Liu3f416732009-07-02 16:36:02 +080096 */
97 public interface ServiceListener {
98 /**
99 * Called to notify the client when this proxy object has been
100 * connected to the BluetoothPbap service. Clients must wait for
101 * this callback before making IPC calls on the BluetoothPbap
102 * service.
103 */
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700104 public void onServiceConnected(BluetoothPbap proxy);
Jiafa Liu3f416732009-07-02 16:36:02 +0800105
106 /**
107 * Called to notify the client that this proxy object has been
108 * disconnected from the BluetoothPbap service. Clients must not
109 * make IPC calls on the BluetoothPbap service after this callback.
110 * This callback will currently only occur if the application hosting
111 * the BluetoothPbap service, but may be called more often in future.
112 */
113 public void onServiceDisconnected();
114 }
115
Jack He2992cd02017-08-22 21:21:23 -0700116 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700117 new IBluetoothStateChangeCallback.Stub() {
118 public void onBluetoothStateChange(boolean up) {
Hansong Zhangfef6d812017-12-08 16:05:55 -0800119 log("onBluetoothStateChange: up=" + up);
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700120 if (!up) {
Hansong Zhangfef6d812017-12-08 16:05:55 -0800121 log("Unbinding service...");
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700122 synchronized (mConnection) {
123 try {
124 mService = null;
125 mContext.unbindService(mConnection);
126 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700127 Log.e(TAG, "", re);
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700128 }
129 }
130 } else {
131 synchronized (mConnection) {
132 try {
133 if (mService == null) {
Hansong Zhangfef6d812017-12-08 16:05:55 -0800134 log("Binding service...");
Dianne Hackborn221ea892013-08-04 16:50:16 -0700135 doBind();
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700136 }
137 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700138 Log.e(TAG, "", re);
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700139 }
140 }
141 }
142 }
Jack Hea355e5e2017-08-22 16:06:54 -0700143 };
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700144
Jiafa Liu3f416732009-07-02 16:36:02 +0800145 /**
Jackson Fanc8b04a92009-07-28 12:15:49 +0800146 * Create a BluetoothPbap proxy object.
Jiafa Liu3f416732009-07-02 16:36:02 +0800147 */
148 public BluetoothPbap(Context context, ServiceListener l) {
149 mContext = context;
150 mServiceListener = l;
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700151 mAdapter = BluetoothAdapter.getDefaultAdapter();
152 IBluetoothManager mgr = mAdapter.getBluetoothManager();
153 if (mgr != null) {
154 try {
155 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
156 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700157 Log.e(TAG, "", e);
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700158 }
159 }
Dianne Hackborn221ea892013-08-04 16:50:16 -0700160 doBind();
161 }
162
163 boolean doBind() {
164 Intent intent = new Intent(IBluetoothPbap.class.getName());
165 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
166 intent.setComponent(comp);
Dianne Hackborn466ce962014-03-19 18:06:58 -0700167 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
jovanak2a9131f2018-10-15 17:50:19 -0700168 UserHandle.CURRENT_OR_SELF)) {
Dianne Hackborn221ea892013-08-04 16:50:16 -0700169 Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
170 return false;
Jiafa Liu3f416732009-07-02 16:36:02 +0800171 }
Dianne Hackborn221ea892013-08-04 16:50:16 -0700172 return true;
Jiafa Liu3f416732009-07-02 16:36:02 +0800173 }
174
175 protected void finalize() throws Throwable {
176 try {
177 close();
178 } finally {
179 super.finalize();
180 }
181 }
182
183 /**
184 * Close the connection to the backing service.
Jackson Fanc8b04a92009-07-28 12:15:49 +0800185 * Other public functions of BluetoothPbap will return default error
Jiafa Liu3f416732009-07-02 16:36:02 +0800186 * results once close() has been called. Multiple invocations of close()
187 * are ok.
188 */
189 public synchronized void close() {
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700190 IBluetoothManager mgr = mAdapter.getBluetoothManager();
191 if (mgr != null) {
192 try {
193 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
194 } catch (Exception e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700195 Log.e(TAG, "", e);
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700196 }
197 }
198
199 synchronized (mConnection) {
200 if (mService != null) {
201 try {
202 mService = null;
203 mContext.unbindService(mConnection);
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700204 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700205 Log.e(TAG, "", re);
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700206 }
207 }
Jiafa Liu3f416732009-07-02 16:36:02 +0800208 }
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800209 mServiceListener = null;
Jiafa Liu3f416732009-07-02 16:36:02 +0800210 }
211
212 /**
Hansong Zhangfef6d812017-12-08 16:05:55 -0800213 * {@inheritDoc}
Jiafa Liu3f416732009-07-02 16:36:02 +0800214 */
Hansong Zhangfef6d812017-12-08 16:05:55 -0800215 @Override
216 public List<BluetoothDevice> getConnectedDevices() {
217 log("getConnectedDevices()");
Jack He16eeac32017-08-17 12:11:18 -0700218 final IBluetoothPbap service = mService;
Hansong Zhangfef6d812017-12-08 16:05:55 -0800219 if (service == null) {
Jiafa Liu3f416732009-07-02 16:36:02 +0800220 Log.w(TAG, "Proxy not attached to service");
Hansong Zhangfef6d812017-12-08 16:05:55 -0800221 return new ArrayList<BluetoothDevice>();
222 }
223 try {
224 return service.getConnectedDevices();
225 } catch (RemoteException e) {
226 Log.e(TAG, e.toString());
227 }
228 return new ArrayList<BluetoothDevice>();
229 }
230
231 /**
232 * {@inheritDoc}
233 */
234 @Override
235 public int getConnectionState(BluetoothDevice device) {
236 log("getConnectionState: device=" + device);
237 final IBluetoothPbap service = mService;
238 if (service == null) {
239 Log.w(TAG, "Proxy not attached to service");
240 return BluetoothProfile.STATE_DISCONNECTED;
241 }
242 try {
243 return service.getConnectionState(device);
244 } catch (RemoteException e) {
245 Log.e(TAG, e.toString());
Jiafa Liu3f416732009-07-02 16:36:02 +0800246 }
Jack He37ab8152017-10-02 19:08:30 -0700247 return BluetoothProfile.STATE_DISCONNECTED;
Jiafa Liu3f416732009-07-02 16:36:02 +0800248 }
249
250 /**
Hansong Zhangfef6d812017-12-08 16:05:55 -0800251 * {@inheritDoc}
Jiafa Liu3f416732009-07-02 16:36:02 +0800252 */
Hansong Zhangfef6d812017-12-08 16:05:55 -0800253 @Override
254 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
255 log("getDevicesMatchingConnectionStates: states=" + Arrays.toString(states));
Jack He16eeac32017-08-17 12:11:18 -0700256 final IBluetoothPbap service = mService;
Hansong Zhangfef6d812017-12-08 16:05:55 -0800257 if (service == null) {
Jiafa Liu3f416732009-07-02 16:36:02 +0800258 Log.w(TAG, "Proxy not attached to service");
Hansong Zhangfef6d812017-12-08 16:05:55 -0800259 return new ArrayList<BluetoothDevice>();
Jiafa Liu3f416732009-07-02 16:36:02 +0800260 }
Hansong Zhangfef6d812017-12-08 16:05:55 -0800261 try {
262 return service.getDevicesMatchingConnectionStates(states);
263 } catch (RemoteException e) {
264 Log.e(TAG, e.toString());
265 }
266 return new ArrayList<BluetoothDevice>();
Jiafa Liu3f416732009-07-02 16:36:02 +0800267 }
268
269 /**
Nick Pellybd022f42009-08-14 18:33:38 -0700270 * Returns true if the specified Bluetooth device is connected (does not
271 * include connecting). Returns false if not connected, or if this proxy
272 * object is not currently connected to the Pbap service.
Jiafa Liu3f416732009-07-02 16:36:02 +0800273 */
Hansong Zhangfef6d812017-12-08 16:05:55 -0800274 // TODO: This is currently being used by SettingsLib and internal app.
Nick Pellybd022f42009-08-14 18:33:38 -0700275 public boolean isConnected(BluetoothDevice device) {
Hansong Zhangfef6d812017-12-08 16:05:55 -0800276 return getConnectionState(device) == BluetoothAdapter.STATE_CONNECTED;
Jiafa Liu3f416732009-07-02 16:36:02 +0800277 }
278
279 /**
Nick Pellybd022f42009-08-14 18:33:38 -0700280 * Disconnects the current Pbap client (PCE). Currently this call blocks,
Jake Hambyf51eada2010-09-21 13:39:53 -0700281 * it may soon be made asynchronous. Returns false if this proxy object is
Jackson Fanc8b04a92009-07-28 12:15:49 +0800282 * not currently connected to the Pbap service.
Jiafa Liu3f416732009-07-02 16:36:02 +0800283 */
Hansong Zhangfef6d812017-12-08 16:05:55 -0800284 // TODO: This is currently being used by SettingsLib and will be used in the future.
285 // TODO: Must specify target device. Implement this in the service.
Mathew Inwood7acad5e2018-08-01 15:00:35 +0100286 @UnsupportedAppUsage
Hansong Zhangfef6d812017-12-08 16:05:55 -0800287 public boolean disconnect(BluetoothDevice device) {
288 log("disconnect()");
Jack He16eeac32017-08-17 12:11:18 -0700289 final IBluetoothPbap service = mService;
Hansong Zhangfef6d812017-12-08 16:05:55 -0800290 if (service == null) {
Jiafa Liu3f416732009-07-02 16:36:02 +0800291 Log.w(TAG, "Proxy not attached to service");
Hansong Zhangfef6d812017-12-08 16:05:55 -0800292 return false;
293 }
294 try {
295 service.disconnect(device);
296 return true;
297 } catch (RemoteException e) {
298 Log.e(TAG, e.toString());
Jiafa Liu3f416732009-07-02 16:36:02 +0800299 }
300 return false;
301 }
302
Matthew Xie9b693992013-10-10 11:21:40 -0700303 private final ServiceConnection mConnection = new ServiceConnection() {
Jiafa Liu3f416732009-07-02 16:36:02 +0800304 public void onServiceConnected(ComponentName className, IBinder service) {
Hansong Zhangfef6d812017-12-08 16:05:55 -0800305 log("Proxy object connected");
Jiafa Liu3f416732009-07-02 16:36:02 +0800306 mService = IBluetoothPbap.Stub.asInterface(service);
307 if (mServiceListener != null) {
Ganesh Ganapathi Batta6f6c54512012-07-31 16:08:17 -0700308 mServiceListener.onServiceConnected(BluetoothPbap.this);
Jiafa Liu3f416732009-07-02 16:36:02 +0800309 }
310 }
Jack Hea355e5e2017-08-22 16:06:54 -0700311
Jiafa Liu3f416732009-07-02 16:36:02 +0800312 public void onServiceDisconnected(ComponentName className) {
Hansong Zhangfef6d812017-12-08 16:05:55 -0800313 log("Proxy object disconnected");
Jiafa Liu3f416732009-07-02 16:36:02 +0800314 mService = null;
315 if (mServiceListener != null) {
316 mServiceListener.onServiceDisconnected();
317 }
318 }
319 };
320
321 private static void log(String msg) {
Hansong Zhangfef6d812017-12-08 16:05:55 -0800322 if (DBG) {
323 Log.d(TAG, msg);
324 }
Jiafa Liu3f416732009-07-02 16:36:02 +0800325 }
326}