blob: dd2f150ad4eb7af789056d53256df5789a1204bd [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
Mathew Inwood4dc66d32018-08-01 15:07:20 +010019import android.annotation.UnsupportedAppUsage;
Matthew Xiefe3807a2013-07-18 17:31:50 -070020import android.content.Context;
Jack Hea355e5e2017-08-22 16:06:54 -070021import android.os.Binder;
22import android.os.IBinder;
23import android.os.RemoteException;
Matthew Xiefe3807a2013-07-18 17:31:50 -070024import android.util.Log;
25
Jack Hea355e5e2017-08-22 16:06:54 -070026import java.util.ArrayList;
27import java.util.List;
28
Matthew Xiefe3807a2013-07-18 17:31:50 -070029/**
30 * This class provides the APIs to control the Bluetooth MAP
31 * Profile.
Jack Hea355e5e2017-08-22 16:06:54 -070032 *
33 * @hide
Matthew Xiefe3807a2013-07-18 17:31:50 -070034 */
Kim Schulz0d376052013-08-22 11:18:02 +020035public final class BluetoothMap implements BluetoothProfile {
Matthew Xiefe3807a2013-07-18 17:31:50 -070036
37 private static final String TAG = "BluetoothMap";
38 private static final boolean DBG = true;
39 private static final boolean VDBG = false;
40
Kim Schulz0d376052013-08-22 11:18:02 +020041 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070042 "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
Matthew Xiefe3807a2013-07-18 17:31:50 -070043
Matthew Xiefe3807a2013-07-18 17:31:50 -070044 /** There was an error trying to obtain the state */
Jack Hea355e5e2017-08-22 16:06:54 -070045 public static final int STATE_ERROR = -1;
Matthew Xiefe3807a2013-07-18 17:31:50 -070046
47 public static final int RESULT_FAILURE = 0;
48 public static final int RESULT_SUCCESS = 1;
49 /** Connection canceled before completion. */
50 public static final int RESULT_CANCELED = 2;
51
Ugo Yud0115462019-03-26 21:38:08 +080052 private BluetoothAdapter mAdapter;
53 private final BluetoothProfileConnector<IBluetoothMap> mProfileConnector =
54 new BluetoothProfileConnector(this, BluetoothProfile.MAP,
55 "BluetoothMap", IBluetoothMap.class.getName()) {
56 @Override
57 public IBluetoothMap getServiceInterface(IBinder service) {
58 return IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service));
Matthew Xiefe3807a2013-07-18 17:31:50 -070059 }
Ugo Yud0115462019-03-26 21:38:08 +080060 };
Matthew Xiefe3807a2013-07-18 17:31:50 -070061
62 /**
63 * Create a BluetoothMap proxy object.
64 */
Ugo Yud0115462019-03-26 21:38:08 +080065 /*package*/ BluetoothMap(Context context, ServiceListener listener) {
Kim Schulz0d376052013-08-22 11:18:02 +020066 if (DBG) Log.d(TAG, "Create BluetoothMap proxy object");
Matthew Xiefe3807a2013-07-18 17:31:50 -070067 mAdapter = BluetoothAdapter.getDefaultAdapter();
Ugo Yud0115462019-03-26 21:38:08 +080068 mProfileConnector.connect(context, listener);
Matthew Xiefe3807a2013-07-18 17:31:50 -070069 }
70
71 protected void finalize() throws Throwable {
72 try {
73 close();
74 } finally {
75 super.finalize();
76 }
77 }
78
79 /**
80 * Close the connection to the backing service.
81 * Other public functions of BluetoothMap will return default error
82 * results once close() has been called. Multiple invocations of close()
83 * are ok.
84 */
85 public synchronized void close() {
Ugo Yud0115462019-03-26 21:38:08 +080086 mProfileConnector.disconnect();
87 }
Matthew Xiefe3807a2013-07-18 17:31:50 -070088
Ugo Yud0115462019-03-26 21:38:08 +080089 private IBluetoothMap getService() {
90 return mProfileConnector.getService();
Matthew Xiefe3807a2013-07-18 17:31:50 -070091 }
92
93 /**
94 * Get the current state of the BluetoothMap service.
Jack Hea355e5e2017-08-22 16:06:54 -070095 *
96 * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not
97 * connected to the Map service.
Matthew Xiefe3807a2013-07-18 17:31:50 -070098 */
99 public int getState() {
100 if (VDBG) log("getState()");
Ugo Yud0115462019-03-26 21:38:08 +0800101 final IBluetoothMap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700102 if (service != null) {
Matthew Xiefe3807a2013-07-18 17:31:50 -0700103 try {
Jack He16eeac32017-08-17 12:11:18 -0700104 return service.getState();
Jack Hea355e5e2017-08-22 16:06:54 -0700105 } catch (RemoteException e) {
106 Log.e(TAG, e.toString());
107 }
Matthew Xiefe3807a2013-07-18 17:31:50 -0700108 } else {
109 Log.w(TAG, "Proxy not attached to service");
110 if (DBG) log(Log.getStackTraceString(new Throwable()));
111 }
112 return BluetoothMap.STATE_ERROR;
113 }
114
115 /**
116 * Get the currently connected remote Bluetooth device (PCE).
Jack Hea355e5e2017-08-22 16:06:54 -0700117 *
118 * @return The remote Bluetooth device, or null if not in connected or connecting state, or if
119 * this proxy object is not connected to the Map service.
Matthew Xiefe3807a2013-07-18 17:31:50 -0700120 */
121 public BluetoothDevice getClient() {
122 if (VDBG) log("getClient()");
Ugo Yud0115462019-03-26 21:38:08 +0800123 final IBluetoothMap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700124 if (service != null) {
Matthew Xiefe3807a2013-07-18 17:31:50 -0700125 try {
Jack He16eeac32017-08-17 12:11:18 -0700126 return service.getClient();
Jack Hea355e5e2017-08-22 16:06:54 -0700127 } catch (RemoteException e) {
128 Log.e(TAG, e.toString());
129 }
Matthew Xiefe3807a2013-07-18 17:31:50 -0700130 } else {
131 Log.w(TAG, "Proxy not attached to service");
132 if (DBG) log(Log.getStackTraceString(new Throwable()));
133 }
134 return null;
135 }
136
137 /**
Kim Schulz0d376052013-08-22 11:18:02 +0200138 * Returns true if the specified Bluetooth device is connected.
139 * Returns false if not connected, or if this proxy object is not
140 * currently connected to the Map service.
Matthew Xiefe3807a2013-07-18 17:31:50 -0700141 */
142 public boolean isConnected(BluetoothDevice device) {
143 if (VDBG) log("isConnected(" + device + ")");
Ugo Yud0115462019-03-26 21:38:08 +0800144 final IBluetoothMap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700145 if (service != null) {
Matthew Xiefe3807a2013-07-18 17:31:50 -0700146 try {
Jack He16eeac32017-08-17 12:11:18 -0700147 return service.isConnected(device);
Jack Hea355e5e2017-08-22 16:06:54 -0700148 } catch (RemoteException e) {
149 Log.e(TAG, e.toString());
150 }
Matthew Xiefe3807a2013-07-18 17:31:50 -0700151 } else {
152 Log.w(TAG, "Proxy not attached to service");
153 if (DBG) log(Log.getStackTraceString(new Throwable()));
154 }
155 return false;
156 }
157
158 /**
Kim Schulz0d376052013-08-22 11:18:02 +0200159 * Initiate connection. Initiation of outgoing connections is not
160 * supported for MAP server.
Matthew Xiefe3807a2013-07-18 17:31:50 -0700161 */
Kim Schulz0d376052013-08-22 11:18:02 +0200162 public boolean connect(BluetoothDevice device) {
163 if (DBG) log("connect(" + device + ")" + "not supported for MAPS");
164 return false;
165 }
166
167 /**
168 * Initiate disconnect.
169 *
170 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700171 * @return false on error, true otherwise
Kim Schulz0d376052013-08-22 11:18:02 +0200172 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100173 @UnsupportedAppUsage
Kim Schulz0d376052013-08-22 11:18:02 +0200174 public boolean disconnect(BluetoothDevice device) {
175 if (DBG) log("disconnect(" + device + ")");
Ugo Yud0115462019-03-26 21:38:08 +0800176 final IBluetoothMap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700177 if (service != null && isEnabled() && isValidDevice(device)) {
Matthew Xiefe3807a2013-07-18 17:31:50 -0700178 try {
Jack He16eeac32017-08-17 12:11:18 -0700179 return service.disconnect(device);
Kim Schulz0d376052013-08-22 11:18:02 +0200180 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700181 Log.e(TAG, Log.getStackTraceString(new Throwable()));
182 return false;
Kim Schulz0d376052013-08-22 11:18:02 +0200183 }
Matthew Xiefe3807a2013-07-18 17:31:50 -0700184 }
Jack He16eeac32017-08-17 12:11:18 -0700185 if (service == null) Log.w(TAG, "Proxy not attached to service");
Matthew Xiefe3807a2013-07-18 17:31:50 -0700186 return false;
187 }
188
189 /**
190 * Check class bits for possible Map support.
191 * This is a simple heuristic that tries to guess if a device with the
192 * given class bits might support Map. It is not accurate for all
193 * devices. It tries to err on the side of false positives.
Jack Hea355e5e2017-08-22 16:06:54 -0700194 *
Matthew Xiefe3807a2013-07-18 17:31:50 -0700195 * @return True if this device might support Map.
196 */
197 public static boolean doesClassMatchSink(BluetoothClass btClass) {
198 // TODO optimize the rule
199 switch (btClass.getDeviceClass()) {
Jack Hea355e5e2017-08-22 16:06:54 -0700200 case BluetoothClass.Device.COMPUTER_DESKTOP:
201 case BluetoothClass.Device.COMPUTER_LAPTOP:
202 case BluetoothClass.Device.COMPUTER_SERVER:
203 case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
204 return true;
205 default:
206 return false;
Matthew Xiefe3807a2013-07-18 17:31:50 -0700207 }
208 }
209
Kim Schulz0d376052013-08-22 11:18:02 +0200210 /**
211 * Get the list of connected devices. Currently at most one.
212 *
213 * @return list of connected devices
214 */
215 public List<BluetoothDevice> getConnectedDevices() {
216 if (DBG) log("getConnectedDevices()");
Ugo Yud0115462019-03-26 21:38:08 +0800217 final IBluetoothMap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700218 if (service != null && isEnabled()) {
Kim Schulz0d376052013-08-22 11:18:02 +0200219 try {
Jack He16eeac32017-08-17 12:11:18 -0700220 return service.getConnectedDevices();
Kim Schulz0d376052013-08-22 11:18:02 +0200221 } catch (RemoteException e) {
222 Log.e(TAG, Log.getStackTraceString(new Throwable()));
223 return new ArrayList<BluetoothDevice>();
224 }
225 }
Jack He16eeac32017-08-17 12:11:18 -0700226 if (service == null) Log.w(TAG, "Proxy not attached to service");
Kim Schulz0d376052013-08-22 11:18:02 +0200227 return new ArrayList<BluetoothDevice>();
228 }
229
230 /**
231 * Get the list of devices matching specified states. Currently at most one.
232 *
233 * @return list of matching devices
234 */
235 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
236 if (DBG) log("getDevicesMatchingStates()");
Ugo Yud0115462019-03-26 21:38:08 +0800237 final IBluetoothMap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700238 if (service != null && isEnabled()) {
Kim Schulz0d376052013-08-22 11:18:02 +0200239 try {
Jack He16eeac32017-08-17 12:11:18 -0700240 return service.getDevicesMatchingConnectionStates(states);
Kim Schulz0d376052013-08-22 11:18:02 +0200241 } catch (RemoteException e) {
242 Log.e(TAG, Log.getStackTraceString(new Throwable()));
243 return new ArrayList<BluetoothDevice>();
244 }
245 }
Jack He16eeac32017-08-17 12:11:18 -0700246 if (service == null) Log.w(TAG, "Proxy not attached to service");
Kim Schulz0d376052013-08-22 11:18:02 +0200247 return new ArrayList<BluetoothDevice>();
248 }
249
250 /**
251 * Get connection state of device
252 *
253 * @return device connection state
254 */
255 public int getConnectionState(BluetoothDevice device) {
256 if (DBG) log("getConnectionState(" + device + ")");
Ugo Yud0115462019-03-26 21:38:08 +0800257 final IBluetoothMap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700258 if (service != null && isEnabled() && isValidDevice(device)) {
Kim Schulz0d376052013-08-22 11:18:02 +0200259 try {
Jack He16eeac32017-08-17 12:11:18 -0700260 return service.getConnectionState(device);
Kim Schulz0d376052013-08-22 11:18:02 +0200261 } catch (RemoteException e) {
262 Log.e(TAG, Log.getStackTraceString(new Throwable()));
263 return BluetoothProfile.STATE_DISCONNECTED;
264 }
265 }
Jack He16eeac32017-08-17 12:11:18 -0700266 if (service == null) Log.w(TAG, "Proxy not attached to service");
Kim Schulz0d376052013-08-22 11:18:02 +0200267 return BluetoothProfile.STATE_DISCONNECTED;
268 }
269
270 /**
271 * Set priority of the profile
272 *
273 * <p> The device should already be paired.
Jack Hea355e5e2017-08-22 16:06:54 -0700274 * Priority can be one of {@link #PRIORITY_ON} or
Kim Schulz0d376052013-08-22 11:18:02 +0200275 * {@link #PRIORITY_OFF},
276 *
277 * @param device Paired bluetooth device
278 * @param priority
279 * @return true if priority is set, false on error
280 */
281 public boolean setPriority(BluetoothDevice device, int priority) {
282 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Ugo Yud0115462019-03-26 21:38:08 +0800283 final IBluetoothMap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700284 if (service != null && isEnabled() && isValidDevice(device)) {
Jack He2992cd02017-08-22 21:21:23 -0700285 if (priority != BluetoothProfile.PRIORITY_OFF
286 && priority != BluetoothProfile.PRIORITY_ON) {
Jack Hea355e5e2017-08-22 16:06:54 -0700287 return false;
Kim Schulz0d376052013-08-22 11:18:02 +0200288 }
289 try {
Jack He16eeac32017-08-17 12:11:18 -0700290 return service.setPriority(device, priority);
Kim Schulz0d376052013-08-22 11:18:02 +0200291 } catch (RemoteException e) {
292 Log.e(TAG, Log.getStackTraceString(new Throwable()));
293 return false;
294 }
295 }
Jack He16eeac32017-08-17 12:11:18 -0700296 if (service == null) Log.w(TAG, "Proxy not attached to service");
Kim Schulz0d376052013-08-22 11:18:02 +0200297 return false;
298 }
299
300 /**
301 * Get the priority of the profile.
302 *
303 * <p> The priority can be any of:
304 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
305 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
306 *
307 * @param device Bluetooth device
308 * @return priority of the device
309 */
310 public int getPriority(BluetoothDevice device) {
311 if (VDBG) log("getPriority(" + device + ")");
Ugo Yud0115462019-03-26 21:38:08 +0800312 final IBluetoothMap service = getService();
Jack He16eeac32017-08-17 12:11:18 -0700313 if (service != null && isEnabled() && isValidDevice(device)) {
Kim Schulz0d376052013-08-22 11:18:02 +0200314 try {
Jack He16eeac32017-08-17 12:11:18 -0700315 return service.getPriority(device);
Kim Schulz0d376052013-08-22 11:18:02 +0200316 } catch (RemoteException e) {
317 Log.e(TAG, Log.getStackTraceString(new Throwable()));
318 return PRIORITY_OFF;
319 }
320 }
Jack He16eeac32017-08-17 12:11:18 -0700321 if (service == null) Log.w(TAG, "Proxy not attached to service");
Kim Schulz0d376052013-08-22 11:18:02 +0200322 return PRIORITY_OFF;
323 }
324
Matthew Xiefe3807a2013-07-18 17:31:50 -0700325 private static void log(String msg) {
326 Log.d(TAG, msg);
327 }
Kim Schulz0d376052013-08-22 11:18:02 +0200328
Jack Hea355e5e2017-08-22 16:06:54 -0700329 private boolean isEnabled() {
Kim Schulz0d376052013-08-22 11:18:02 +0200330 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
331 if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
332 log("Bluetooth is Not enabled");
333 return false;
334 }
Jack He16eeac32017-08-17 12:11:18 -0700335 private static boolean isValidDevice(BluetoothDevice device) {
336 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
Kim Schulz0d376052013-08-22 11:18:02 +0200337 }
338
Matthew Xiefe3807a2013-07-18 17:31:50 -0700339}