blob: 979dfd4e3ba58fa0044f284214bfc0d659d9b567 [file] [log] [blame]
Matthew Xiefe3807a2013-07-18 17:31:50 -07001/*
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
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -080019import android.Manifest;
20import android.annotation.RequiresPermission;
21import android.annotation.SystemApi;
Mathew Inwood4dc66d32018-08-01 15:07:20 +010022import android.annotation.UnsupportedAppUsage;
Matthew Xiefe3807a2013-07-18 17:31:50 -070023import android.content.Context;
Jack Hea355e5e2017-08-22 16:06:54 -070024import android.os.Binder;
25import android.os.IBinder;
26import android.os.RemoteException;
Matthew Xiefe3807a2013-07-18 17:31:50 -070027import android.util.Log;
28
Jack Hea355e5e2017-08-22 16:06:54 -070029import java.util.ArrayList;
30import java.util.List;
31
Matthew Xiefe3807a2013-07-18 17:31:50 -070032/**
33 * This class provides the APIs to control the Bluetooth MAP
34 * Profile.
Jack Hea355e5e2017-08-22 16:06:54 -070035 *
36 * @hide
Matthew Xiefe3807a2013-07-18 17:31:50 -070037 */
Kim Schulz0d376052013-08-22 11:18:02 +020038public final class BluetoothMap implements BluetoothProfile {
Matthew Xiefe3807a2013-07-18 17:31:50 -070039
40 private static final String TAG = "BluetoothMap";
41 private static final boolean DBG = true;
42 private static final boolean VDBG = false;
43
Kim Schulz0d376052013-08-22 11:18:02 +020044 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070045 "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
Matthew Xiefe3807a2013-07-18 17:31:50 -070046
Matthew Xiefe3807a2013-07-18 17:31:50 -070047 /** There was an error trying to obtain the state */
Jack Hea355e5e2017-08-22 16:06:54 -070048 public static final int STATE_ERROR = -1;
Matthew Xiefe3807a2013-07-18 17:31:50 -070049
50 public static final int RESULT_FAILURE = 0;
51 public static final int RESULT_SUCCESS = 1;
52 /** Connection canceled before completion. */
53 public static final int RESULT_CANCELED = 2;
54
Ugo Yud0115462019-03-26 21:38:08 +080055 private BluetoothAdapter mAdapter;
56 private final BluetoothProfileConnector<IBluetoothMap> mProfileConnector =
57 new BluetoothProfileConnector(this, BluetoothProfile.MAP,
58 "BluetoothMap", IBluetoothMap.class.getName()) {
59 @Override
60 public IBluetoothMap getServiceInterface(IBinder service) {
61 return IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service));
Matthew Xiefe3807a2013-07-18 17:31:50 -070062 }
Ugo Yud0115462019-03-26 21:38:08 +080063 };
Matthew Xiefe3807a2013-07-18 17:31:50 -070064
65 /**
66 * Create a BluetoothMap proxy object.
67 */
Ugo Yud0115462019-03-26 21:38:08 +080068 /*package*/ BluetoothMap(Context context, ServiceListener listener) {
Kim Schulz0d376052013-08-22 11:18:02 +020069 if (DBG) Log.d(TAG, "Create BluetoothMap proxy object");
Matthew Xiefe3807a2013-07-18 17:31:50 -070070 mAdapter = BluetoothAdapter.getDefaultAdapter();
Ugo Yud0115462019-03-26 21:38:08 +080071 mProfileConnector.connect(context, listener);
Matthew Xiefe3807a2013-07-18 17:31:50 -070072 }
73
74 protected void finalize() throws Throwable {
75 try {
76 close();
77 } finally {
78 super.finalize();
79 }
80 }
81
82 /**
83 * Close the connection to the backing service.
84 * Other public functions of BluetoothMap will return default error
85 * results once close() has been called. Multiple invocations of close()
86 * are ok.
87 */
88 public synchronized void close() {
Ugo Yud0115462019-03-26 21:38:08 +080089 mProfileConnector.disconnect();
90 }
Matthew Xiefe3807a2013-07-18 17:31:50 -070091
Ugo Yud0115462019-03-26 21:38:08 +080092 private IBluetoothMap getService() {
93 return mProfileConnector.getService();
Matthew Xiefe3807a2013-07-18 17:31:50 -070094 }
95
96 /**
97 * Get the current state of the BluetoothMap service.
Jack Hea355e5e2017-08-22 16:06:54 -070098 *
99 * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not
100 * connected to the Map service.
Matthew Xiefe3807a2013-07-18 17:31:50 -0700101 */
102 public int getState() {
103 if (VDBG) log("getState()");
Ugo Yud0115462019-03-26 21:38:08 +0800104 final IBluetoothMap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700105 if (service != null) {
Matthew Xiefe3807a2013-07-18 17:31:50 -0700106 try {
Jack He16eeac32017-08-17 12:11:18 -0700107 return service.getState();
Jack Hea355e5e2017-08-22 16:06:54 -0700108 } catch (RemoteException e) {
109 Log.e(TAG, e.toString());
110 }
Matthew Xiefe3807a2013-07-18 17:31:50 -0700111 } else {
112 Log.w(TAG, "Proxy not attached to service");
113 if (DBG) log(Log.getStackTraceString(new Throwable()));
114 }
115 return BluetoothMap.STATE_ERROR;
116 }
117
118 /**
119 * Get the currently connected remote Bluetooth device (PCE).
Jack Hea355e5e2017-08-22 16:06:54 -0700120 *
121 * @return The remote Bluetooth device, or null if not in connected or connecting state, or if
122 * this proxy object is not connected to the Map service.
Matthew Xiefe3807a2013-07-18 17:31:50 -0700123 */
124 public BluetoothDevice getClient() {
125 if (VDBG) log("getClient()");
Ugo Yud0115462019-03-26 21:38:08 +0800126 final IBluetoothMap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700127 if (service != null) {
Matthew Xiefe3807a2013-07-18 17:31:50 -0700128 try {
Jack He16eeac32017-08-17 12:11:18 -0700129 return service.getClient();
Jack Hea355e5e2017-08-22 16:06:54 -0700130 } catch (RemoteException e) {
131 Log.e(TAG, e.toString());
132 }
Matthew Xiefe3807a2013-07-18 17:31:50 -0700133 } else {
134 Log.w(TAG, "Proxy not attached to service");
135 if (DBG) log(Log.getStackTraceString(new Throwable()));
136 }
137 return null;
138 }
139
140 /**
Kim Schulz0d376052013-08-22 11:18:02 +0200141 * Returns true if the specified Bluetooth device is connected.
142 * Returns false if not connected, or if this proxy object is not
143 * currently connected to the Map service.
Matthew Xiefe3807a2013-07-18 17:31:50 -0700144 */
145 public boolean isConnected(BluetoothDevice device) {
146 if (VDBG) log("isConnected(" + device + ")");
Ugo Yud0115462019-03-26 21:38:08 +0800147 final IBluetoothMap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700148 if (service != null) {
Matthew Xiefe3807a2013-07-18 17:31:50 -0700149 try {
Jack He16eeac32017-08-17 12:11:18 -0700150 return service.isConnected(device);
Jack Hea355e5e2017-08-22 16:06:54 -0700151 } catch (RemoteException e) {
152 Log.e(TAG, e.toString());
153 }
Matthew Xiefe3807a2013-07-18 17:31:50 -0700154 } else {
155 Log.w(TAG, "Proxy not attached to service");
156 if (DBG) log(Log.getStackTraceString(new Throwable()));
157 }
158 return false;
159 }
160
161 /**
Kim Schulz0d376052013-08-22 11:18:02 +0200162 * Initiate connection. Initiation of outgoing connections is not
163 * supported for MAP server.
Matthew Xiefe3807a2013-07-18 17:31:50 -0700164 */
Kim Schulz0d376052013-08-22 11:18:02 +0200165 public boolean connect(BluetoothDevice device) {
166 if (DBG) log("connect(" + device + ")" + "not supported for MAPS");
167 return false;
168 }
169
170 /**
171 * Initiate disconnect.
172 *
173 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700174 * @return false on error, true otherwise
Kim Schulz0d376052013-08-22 11:18:02 +0200175 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100176 @UnsupportedAppUsage
Kim Schulz0d376052013-08-22 11:18:02 +0200177 public boolean disconnect(BluetoothDevice device) {
178 if (DBG) log("disconnect(" + device + ")");
Ugo Yud0115462019-03-26 21:38:08 +0800179 final IBluetoothMap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700180 if (service != null && isEnabled() && isValidDevice(device)) {
Matthew Xiefe3807a2013-07-18 17:31:50 -0700181 try {
Jack He16eeac32017-08-17 12:11:18 -0700182 return service.disconnect(device);
Kim Schulz0d376052013-08-22 11:18:02 +0200183 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700184 Log.e(TAG, Log.getStackTraceString(new Throwable()));
185 return false;
Kim Schulz0d376052013-08-22 11:18:02 +0200186 }
Matthew Xiefe3807a2013-07-18 17:31:50 -0700187 }
Jack He16eeac32017-08-17 12:11:18 -0700188 if (service == null) Log.w(TAG, "Proxy not attached to service");
Matthew Xiefe3807a2013-07-18 17:31:50 -0700189 return false;
190 }
191
192 /**
193 * Check class bits for possible Map support.
194 * This is a simple heuristic that tries to guess if a device with the
195 * given class bits might support Map. It is not accurate for all
196 * devices. It tries to err on the side of false positives.
Jack Hea355e5e2017-08-22 16:06:54 -0700197 *
Matthew Xiefe3807a2013-07-18 17:31:50 -0700198 * @return True if this device might support Map.
199 */
200 public static boolean doesClassMatchSink(BluetoothClass btClass) {
201 // TODO optimize the rule
202 switch (btClass.getDeviceClass()) {
Jack Hea355e5e2017-08-22 16:06:54 -0700203 case BluetoothClass.Device.COMPUTER_DESKTOP:
204 case BluetoothClass.Device.COMPUTER_LAPTOP:
205 case BluetoothClass.Device.COMPUTER_SERVER:
206 case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
207 return true;
208 default:
209 return false;
Matthew Xiefe3807a2013-07-18 17:31:50 -0700210 }
211 }
212
Kim Schulz0d376052013-08-22 11:18:02 +0200213 /**
214 * Get the list of connected devices. Currently at most one.
215 *
216 * @return list of connected devices
217 */
218 public List<BluetoothDevice> getConnectedDevices() {
219 if (DBG) log("getConnectedDevices()");
Ugo Yud0115462019-03-26 21:38:08 +0800220 final IBluetoothMap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700221 if (service != null && isEnabled()) {
Kim Schulz0d376052013-08-22 11:18:02 +0200222 try {
Jack He16eeac32017-08-17 12:11:18 -0700223 return service.getConnectedDevices();
Kim Schulz0d376052013-08-22 11:18:02 +0200224 } catch (RemoteException e) {
225 Log.e(TAG, Log.getStackTraceString(new Throwable()));
226 return new ArrayList<BluetoothDevice>();
227 }
228 }
Jack He16eeac32017-08-17 12:11:18 -0700229 if (service == null) Log.w(TAG, "Proxy not attached to service");
Kim Schulz0d376052013-08-22 11:18:02 +0200230 return new ArrayList<BluetoothDevice>();
231 }
232
233 /**
234 * Get the list of devices matching specified states. Currently at most one.
235 *
236 * @return list of matching devices
237 */
238 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
239 if (DBG) log("getDevicesMatchingStates()");
Ugo Yud0115462019-03-26 21:38:08 +0800240 final IBluetoothMap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700241 if (service != null && isEnabled()) {
Kim Schulz0d376052013-08-22 11:18:02 +0200242 try {
Jack He16eeac32017-08-17 12:11:18 -0700243 return service.getDevicesMatchingConnectionStates(states);
Kim Schulz0d376052013-08-22 11:18:02 +0200244 } catch (RemoteException e) {
245 Log.e(TAG, Log.getStackTraceString(new Throwable()));
246 return new ArrayList<BluetoothDevice>();
247 }
248 }
Jack He16eeac32017-08-17 12:11:18 -0700249 if (service == null) Log.w(TAG, "Proxy not attached to service");
Kim Schulz0d376052013-08-22 11:18:02 +0200250 return new ArrayList<BluetoothDevice>();
251 }
252
253 /**
254 * Get connection state of device
255 *
256 * @return device connection state
257 */
258 public int getConnectionState(BluetoothDevice device) {
259 if (DBG) log("getConnectionState(" + device + ")");
Ugo Yud0115462019-03-26 21:38:08 +0800260 final IBluetoothMap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700261 if (service != null && isEnabled() && isValidDevice(device)) {
Kim Schulz0d376052013-08-22 11:18:02 +0200262 try {
Jack He16eeac32017-08-17 12:11:18 -0700263 return service.getConnectionState(device);
Kim Schulz0d376052013-08-22 11:18:02 +0200264 } catch (RemoteException e) {
265 Log.e(TAG, Log.getStackTraceString(new Throwable()));
266 return BluetoothProfile.STATE_DISCONNECTED;
267 }
268 }
Jack He16eeac32017-08-17 12:11:18 -0700269 if (service == null) Log.w(TAG, "Proxy not attached to service");
Kim Schulz0d376052013-08-22 11:18:02 +0200270 return BluetoothProfile.STATE_DISCONNECTED;
271 }
272
273 /**
274 * Set priority of the profile
275 *
276 * <p> The device should already be paired.
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800277 * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
Kim Schulz0d376052013-08-22 11:18:02 +0200278 *
279 * @param device Paired bluetooth device
280 * @param priority
281 * @return true if priority is set, false on error
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800282 * @hide
Kim Schulz0d376052013-08-22 11:18:02 +0200283 */
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800284 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
Kim Schulz0d376052013-08-22 11:18:02 +0200285 public boolean setPriority(BluetoothDevice device, int priority) {
286 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800287 return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
288 }
289
290 /**
291 * Set connection policy of the profile
292 *
293 * <p> The device should already be paired.
294 * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
295 * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
296 *
297 * @param device Paired bluetooth device
298 * @param connectionPolicy is the connection policy to set to for this profile
299 * @return true if connectionPolicy is set, false on error
300 * @hide
301 */
302 @SystemApi
303 @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
304 public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
305 if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
Ugo Yud0115462019-03-26 21:38:08 +0800306 final IBluetoothMap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700307 if (service != null && isEnabled() && isValidDevice(device)) {
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800308 if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
309 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
Jack Hea355e5e2017-08-22 16:06:54 -0700310 return false;
Kim Schulz0d376052013-08-22 11:18:02 +0200311 }
312 try {
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800313 return service.setConnectionPolicy(device, connectionPolicy);
Kim Schulz0d376052013-08-22 11:18:02 +0200314 } catch (RemoteException e) {
315 Log.e(TAG, Log.getStackTraceString(new Throwable()));
316 return false;
317 }
318 }
Jack He16eeac32017-08-17 12:11:18 -0700319 if (service == null) Log.w(TAG, "Proxy not attached to service");
Kim Schulz0d376052013-08-22 11:18:02 +0200320 return false;
321 }
322
323 /**
324 * Get the priority of the profile.
325 *
326 * <p> The priority can be any of:
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800327 * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
Kim Schulz0d376052013-08-22 11:18:02 +0200328 *
329 * @param device Bluetooth device
330 * @return priority of the device
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800331 * @hide
Kim Schulz0d376052013-08-22 11:18:02 +0200332 */
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800333 @RequiresPermission(Manifest.permission.BLUETOOTH)
Kim Schulz0d376052013-08-22 11:18:02 +0200334 public int getPriority(BluetoothDevice device) {
335 if (VDBG) log("getPriority(" + device + ")");
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800336 return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
337 }
338
339 /**
340 * Get the connection policy of the profile.
341 *
342 * <p> The connection policy can be any of:
343 * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
344 * {@link #CONNECTION_POLICY_UNKNOWN}
345 *
346 * @param device Bluetooth device
347 * @return connection policy of the device
348 * @hide
349 */
350 @SystemApi
351 @RequiresPermission(Manifest.permission.BLUETOOTH)
352 public int getConnectionPolicy(BluetoothDevice device) {
353 if (VDBG) log("getConnectionPolicy(" + device + ")");
Ugo Yud0115462019-03-26 21:38:08 +0800354 final IBluetoothMap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700355 if (service != null && isEnabled() && isValidDevice(device)) {
Kim Schulz0d376052013-08-22 11:18:02 +0200356 try {
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800357 return service.getConnectionPolicy(device);
Kim Schulz0d376052013-08-22 11:18:02 +0200358 } catch (RemoteException e) {
359 Log.e(TAG, Log.getStackTraceString(new Throwable()));
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800360 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
Kim Schulz0d376052013-08-22 11:18:02 +0200361 }
362 }
Jack He16eeac32017-08-17 12:11:18 -0700363 if (service == null) Log.w(TAG, "Proxy not attached to service");
Rahul Sabnisdf1ef4a2019-11-27 18:09:33 -0800364 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
Kim Schulz0d376052013-08-22 11:18:02 +0200365 }
366
Matthew Xiefe3807a2013-07-18 17:31:50 -0700367 private static void log(String msg) {
368 Log.d(TAG, msg);
369 }
Kim Schulz0d376052013-08-22 11:18:02 +0200370
Jack Hea355e5e2017-08-22 16:06:54 -0700371 private boolean isEnabled() {
Kim Schulz0d376052013-08-22 11:18:02 +0200372 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
373 if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
374 log("Bluetooth is Not enabled");
375 return false;
376 }
Jack He16eeac32017-08-17 12:11:18 -0700377 private static boolean isValidDevice(BluetoothDevice device) {
378 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
Kim Schulz0d376052013-08-22 11:18:02 +0200379 }
380
Matthew Xiefe3807a2013-07-18 17:31:50 -0700381}