blob: 69682c6ab5d0df0aa8ab4376351cf03f699a357c [file] [log] [blame]
Rahul Ravikumar05336002019-10-14 15:04:32 -07001/*
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
19import android.annotation.UnsupportedAppUsage;
20import android.app.PendingIntent;
21import android.content.Context;
22import android.net.Uri;
23import android.os.Binder;
24import android.os.IBinder;
25import android.os.RemoteException;
26import android.util.Log;
27
28import java.util.ArrayList;
29import java.util.List;
30
31/**
32 * This class provides the APIs to control the Bluetooth MAP MCE Profile.
33 *
34 * @hide
35 */
36public final class BluetoothMapClient implements BluetoothProfile {
37
38 private static final String TAG = "BluetoothMapClient";
39 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
40 private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
41
42 public static final String ACTION_CONNECTION_STATE_CHANGED =
43 "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED";
44 public static final String ACTION_MESSAGE_RECEIVED =
45 "android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED";
46 /* Actions to be used for pending intents */
47 public static final String ACTION_MESSAGE_SENT_SUCCESSFULLY =
48 "android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY";
49 public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY =
50 "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY";
51
52 /* Extras used in ACTION_MESSAGE_RECEIVED intent.
53 * NOTE: HANDLE is only valid for a single session with the device. */
54 public static final String EXTRA_MESSAGE_HANDLE =
55 "android.bluetooth.mapmce.profile.extra.MESSAGE_HANDLE";
56 public static final String EXTRA_MESSAGE_TIMESTAMP =
57 "android.bluetooth.mapmce.profile.extra.MESSAGE_TIMESTAMP";
58 public static final String EXTRA_MESSAGE_READ_STATUS =
59 "android.bluetooth.mapmce.profile.extra.MESSAGE_READ_STATUS";
60 public static final String EXTRA_SENDER_CONTACT_URI =
61 "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_URI";
62 public static final String EXTRA_SENDER_CONTACT_NAME =
63 "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME";
64
65 /** There was an error trying to obtain the state */
66 public static final int STATE_ERROR = -1;
67
68 public static final int RESULT_FAILURE = 0;
69 public static final int RESULT_SUCCESS = 1;
70 /** Connection canceled before completion. */
71 public static final int RESULT_CANCELED = 2;
72
73 private static final int UPLOADING_FEATURE_BITMASK = 0x08;
74
75 private BluetoothAdapter mAdapter;
76 private final BluetoothProfileConnector<IBluetoothMapClient> mProfileConnector =
77 new BluetoothProfileConnector(this, BluetoothProfile.MAP_CLIENT,
78 "BluetoothMapClient", IBluetoothMapClient.class.getName()) {
79 @Override
80 public IBluetoothMapClient getServiceInterface(IBinder service) {
81 return IBluetoothMapClient.Stub.asInterface(Binder.allowBlocking(service));
82 }
83 };
84
85 /**
86 * Create a BluetoothMapClient proxy object.
87 */
88 /*package*/ BluetoothMapClient(Context context, ServiceListener listener) {
89 if (DBG) Log.d(TAG, "Create BluetoothMapClient proxy object");
90 mAdapter = BluetoothAdapter.getDefaultAdapter();
91 mProfileConnector.connect(context, listener);
92 }
93
94 protected void finalize() throws Throwable {
95 try {
96 close();
97 } finally {
98 super.finalize();
99 }
100 }
101
102 /**
103 * Close the connection to the backing service.
104 * Other public functions of BluetoothMap will return default error
105 * results once close() has been called. Multiple invocations of close()
106 * are ok.
107 */
108 public void close() {
109 mProfileConnector.disconnect();
110 }
111
112 private IBluetoothMapClient getService() {
113 return mProfileConnector.getService();
114 }
115
116 /**
117 * Returns true if the specified Bluetooth device is connected.
118 * Returns false if not connected, or if this proxy object is not
119 * currently connected to the Map service.
120 */
121 public boolean isConnected(BluetoothDevice device) {
122 if (VDBG) Log.d(TAG, "isConnected(" + device + ")");
123 final IBluetoothMapClient service = getService();
124 if (service != null) {
125 try {
126 return service.isConnected(device);
127 } catch (RemoteException e) {
128 Log.e(TAG, e.toString());
129 }
130 } else {
131 Log.w(TAG, "Proxy not attached to service");
132 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
133 }
134 return false;
135 }
136
137 /**
138 * Initiate connection. Initiation of outgoing connections is not
139 * supported for MAP server.
140 */
141 public boolean connect(BluetoothDevice device) {
142 if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE");
143 final IBluetoothMapClient service = getService();
144 if (service != null) {
145 try {
146 return service.connect(device);
147 } catch (RemoteException e) {
148 Log.e(TAG, e.toString());
149 }
150 } else {
151 Log.w(TAG, "Proxy not attached to service");
152 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
153 }
154 return false;
155 }
156
157 /**
158 * Initiate disconnect.
159 *
160 * @param device Remote Bluetooth Device
161 * @return false on error, true otherwise
162 */
163 public boolean disconnect(BluetoothDevice device) {
164 if (DBG) Log.d(TAG, "disconnect(" + device + ")");
165 final IBluetoothMapClient service = getService();
166 if (service != null && isEnabled() && isValidDevice(device)) {
167 try {
168 return service.disconnect(device);
169 } catch (RemoteException e) {
170 Log.e(TAG, Log.getStackTraceString(new Throwable()));
171 }
172 }
173 if (service == null) Log.w(TAG, "Proxy not attached to service");
174 return false;
175 }
176
177 /**
178 * Get the list of connected devices. Currently at most one.
179 *
180 * @return list of connected devices
181 */
182 @Override
183 public List<BluetoothDevice> getConnectedDevices() {
184 if (DBG) Log.d(TAG, "getConnectedDevices()");
185 final IBluetoothMapClient service = getService();
186 if (service != null && isEnabled()) {
187 try {
188 return service.getConnectedDevices();
189 } catch (RemoteException e) {
190 Log.e(TAG, Log.getStackTraceString(new Throwable()));
191 return new ArrayList<>();
192 }
193 }
194 if (service == null) Log.w(TAG, "Proxy not attached to service");
195 return new ArrayList<>();
196 }
197
198 /**
199 * Get the list of devices matching specified states. Currently at most one.
200 *
201 * @return list of matching devices
202 */
203 @Override
204 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
205 if (DBG) Log.d(TAG, "getDevicesMatchingStates()");
206 final IBluetoothMapClient service = getService();
207 if (service != null && isEnabled()) {
208 try {
209 return service.getDevicesMatchingConnectionStates(states);
210 } catch (RemoteException e) {
211 Log.e(TAG, Log.getStackTraceString(new Throwable()));
212 return new ArrayList<>();
213 }
214 }
215 if (service == null) Log.w(TAG, "Proxy not attached to service");
216 return new ArrayList<>();
217 }
218
219 /**
220 * Get connection state of device
221 *
222 * @return device connection state
223 */
224 @Override
225 public int getConnectionState(BluetoothDevice device) {
226 if (DBG) Log.d(TAG, "getConnectionState(" + device + ")");
227 final IBluetoothMapClient service = getService();
228 if (service != null && isEnabled() && isValidDevice(device)) {
229 try {
230 return service.getConnectionState(device);
231 } catch (RemoteException e) {
232 Log.e(TAG, Log.getStackTraceString(new Throwable()));
233 return BluetoothProfile.STATE_DISCONNECTED;
234 }
235 }
236 if (service == null) Log.w(TAG, "Proxy not attached to service");
237 return BluetoothProfile.STATE_DISCONNECTED;
238 }
239
240 /**
241 * Set priority of the profile
242 *
243 * <p> The device should already be paired. Priority can be one of {@link #PRIORITY_ON} or
244 * {@link #PRIORITY_OFF},
245 *
246 * @param device Paired bluetooth device
247 * @return true if priority is set, false on error
248 */
249 public boolean setPriority(BluetoothDevice device, int priority) {
250 if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")");
251 final IBluetoothMapClient service = getService();
252 if (service != null && isEnabled() && isValidDevice(device)) {
253 if (priority != BluetoothProfile.PRIORITY_OFF
254 && priority != BluetoothProfile.PRIORITY_ON) {
255 return false;
256 }
257 try {
258 return service.setPriority(device, priority);
259 } catch (RemoteException e) {
260 Log.e(TAG, Log.getStackTraceString(new Throwable()));
261 return false;
262 }
263 }
264 if (service == null) Log.w(TAG, "Proxy not attached to service");
265 return false;
266 }
267
268 /**
269 * Get the priority of the profile.
270 *
271 * <p> The priority can be any of:
272 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
273 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
274 *
275 * @param device Bluetooth device
276 * @return priority of the device
277 */
278 public int getPriority(BluetoothDevice device) {
279 if (VDBG) Log.d(TAG, "getPriority(" + device + ")");
280 final IBluetoothMapClient service = getService();
281 if (service != null && isEnabled() && isValidDevice(device)) {
282 try {
283 return service.getPriority(device);
284 } catch (RemoteException e) {
285 Log.e(TAG, Log.getStackTraceString(new Throwable()));
286 return PRIORITY_OFF;
287 }
288 }
289 if (service == null) Log.w(TAG, "Proxy not attached to service");
290 return PRIORITY_OFF;
291 }
292
293 /**
294 * Send a message.
295 *
296 * Send an SMS message to either the contacts primary number or the telephone number specified.
297 *
298 * @param device Bluetooth device
299 * @param contacts Uri[] of the contacts
300 * @param message Message to be sent
301 * @param sentIntent intent issued when message is sent
302 * @param deliveredIntent intent issued when message is delivered
303 * @return true if the message is enqueued, false on error
304 */
305 @UnsupportedAppUsage
306 public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message,
307 PendingIntent sentIntent, PendingIntent deliveredIntent) {
308 if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
309 final IBluetoothMapClient service = getService();
310 if (service != null && isEnabled() && isValidDevice(device)) {
311 try {
312 return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent);
313 } catch (RemoteException e) {
314 Log.e(TAG, Log.getStackTraceString(new Throwable()));
315 return false;
316 }
317 }
318 return false;
319 }
320
321 /**
322 * Get unread messages. Unread messages will be published via {@link #ACTION_MESSAGE_RECEIVED}.
323 *
324 * @param device Bluetooth device
325 * @return true if the message is enqueued, false on error
326 */
327 public boolean getUnreadMessages(BluetoothDevice device) {
328 if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")");
329 final IBluetoothMapClient service = getService();
330 if (service != null && isEnabled() && isValidDevice(device)) {
331 try {
332 return service.getUnreadMessages(device);
333 } catch (RemoteException e) {
334 Log.e(TAG, Log.getStackTraceString(new Throwable()));
335 return false;
336 }
337 }
338 return false;
339 }
340
341 /**
342 * Returns the "Uploading" feature bit value from the SDP record's
343 * MapSupportedFeatures field (see Bluetooth MAP 1.4 spec, page 114).
344 * @param device The Bluetooth device to get this value for.
345 * @return Returns true if the Uploading bit value in SDP record's
346 * MapSupportedFeatures field is set. False is returned otherwise.
347 */
348 public boolean isUploadingSupported(BluetoothDevice device) {
349 final IBluetoothMapClient service = getService();
350 try {
351 return (service != null && isEnabled() && isValidDevice(device))
352 && ((service.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0);
353 } catch (RemoteException e) {
354 Log.e(TAG, e.getMessage());
355 }
356 return false;
357 }
358
359 private boolean isEnabled() {
360 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
361 if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
362 if (DBG) Log.d(TAG, "Bluetooth is Not enabled");
363 return false;
364 }
365
366 private static boolean isValidDevice(BluetoothDevice device) {
367 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
368 }
369
370}