1 /* 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 17 /** 18 * Bluetooth MAP MCE StateMachine 19 * (Disconnected) 20 * | ^ 21 * CONNECT | | DISCONNECTED 22 * V | 23 * (Connecting) (Disconnecting) 24 * | ^ 25 * CONNECTED | | DISCONNECT 26 * V | 27 * (Connected) 28 * 29 * Valid Transitions: State + Event -> Transition: 30 * 31 * Disconnected + CONNECT -> Connecting 32 * Connecting + CONNECTED -> Connected 33 * Connecting + TIMEOUT -> Disconnecting 34 * Connecting + DISCONNECT/CONNECT -> Defer Message 35 * Connected + DISCONNECT -> Disconnecting 36 * Connected + CONNECT -> Disconnecting + Defer Message 37 * Disconnecting + DISCONNECTED -> (Safe) Disconnected 38 * Disconnecting + TIMEOUT -> (Force) Disconnected 39 * Disconnecting + DISCONNECT/CONNECT : Defer Message 40 */ 41 package com.android.bluetooth.mapclient; 42 43 import android.app.PendingIntent; 44 import android.bluetooth.BluetoothAdapter; 45 import android.bluetooth.BluetoothDevice; 46 import android.bluetooth.BluetoothMapClient; 47 import android.bluetooth.BluetoothProfile; 48 import android.bluetooth.BluetoothUuid; 49 import android.bluetooth.SdpMasRecord; 50 import android.content.BroadcastReceiver; 51 import android.content.Context; 52 import android.content.Intent; 53 import android.content.IntentFilter; 54 import android.net.Uri; 55 import android.os.Message; 56 import android.os.ParcelUuid; 57 import android.provider.ContactsContract; 58 import android.telecom.PhoneAccount; 59 import android.util.Log; 60 61 import com.android.bluetooth.btservice.ProfileService; 62 import com.android.internal.util.IState; 63 import com.android.internal.util.State; 64 import com.android.internal.util.StateMachine; 65 import com.android.vcard.VCardConstants; 66 import com.android.vcard.VCardEntry; 67 import com.android.vcard.VCardProperty; 68 69 import java.util.ArrayList; 70 import java.util.HashMap; 71 import java.util.List; 72 73 /* The MceStateMachine is responsible for setting up and maintaining a connection to a single 74 * specific Messaging Server Equipment endpoint. Upon connect command an SDP record is retrieved, 75 * a connection to the Message Access Server is created and a request to enable notification of new 76 * messages is sent. 77 */ 78 final class MceStateMachine extends StateMachine { 79 // Messages for events handled by the StateMachine 80 static final int MSG_MAS_CONNECTED = 1001; 81 static final int MSG_MAS_DISCONNECTED = 1002; 82 static final int MSG_MAS_REQUEST_COMPLETED = 1003; 83 static final int MSG_MAS_REQUEST_FAILED = 1004; 84 static final int MSG_MAS_SDP_DONE = 1005; 85 static final int MSG_MAS_SDP_FAILED = 1006; 86 static final int MSG_OUTBOUND_MESSAGE = 2001; 87 static final int MSG_INBOUND_MESSAGE = 2002; 88 static final int MSG_NOTIFICATION = 2003; 89 static final int MSG_GET_LISTING = 2004; 90 static final int MSG_GET_MESSAGE_LISTING = 2005; 91 92 private static final String TAG = "MceSM"; 93 private static final Boolean DBG = MapClientService.DBG; 94 private static final int TIMEOUT = 10000; 95 private static final int MAX_MESSAGES = 20; 96 private static final int MSG_CONNECT = 1; 97 private static final int MSG_DISCONNECT = 2; 98 private static final int MSG_CONNECTING_TIMEOUT = 3; 99 private static final int MSG_DISCONNECTING_TIMEOUT = 4; 100 // Folder names as defined in Bluetooth.org MAP spec V10 101 private static final String FOLDER_TELECOM = "telecom"; 102 private static final String FOLDER_MSG = "msg"; 103 private static final String FOLDER_OUTBOX = "outbox"; 104 private static final String FOLDER_INBOX = "inbox"; 105 private static final String INBOX_PATH = "telecom/msg/inbox"; 106 107 108 // Connectivity States 109 private int mPreviousState = BluetoothProfile.STATE_DISCONNECTED; 110 private State mDisconnected; 111 private State mConnecting; 112 private State mConnected; 113 private State mDisconnecting; 114 115 private BluetoothDevice mDevice; 116 private MapClientService mService; 117 private MasClient mMasClient; 118 private HashMap<String, Bmessage> sentMessageLog = 119 new HashMap<>(MAX_MESSAGES); 120 private HashMap<Bmessage, PendingIntent> sentReceiptRequested = new HashMap<>( 121 MAX_MESSAGES); 122 private HashMap<Bmessage, PendingIntent> deliveryReceiptRequested = new HashMap<>( 123 MAX_MESSAGES); 124 private Bmessage.Type mDefaultMessageType = Bmessage.Type.SMS_CDMA; 125 private MapBroadcastReceiver mMapReceiver = new MapBroadcastReceiver(); 126 127 MceStateMachine(MapClientService service) { 128 super(TAG); 129 mService = service; 130 131 mPreviousState = BluetoothProfile.STATE_DISCONNECTED; 132 133 mDisconnected = new Disconnected(); 134 mConnecting = new Connecting(); 135 mDisconnecting = new Disconnecting(); 136 mConnected = new Connected(); 137 138 addState(mDisconnected); 139 addState(mConnecting); 140 addState(mDisconnecting); 141 addState(mConnected); 142 setInitialState(mDisconnected); 143 start(); 144 } 145 146 public void doQuit() { 147 quitNow(); 148 } 149 150 synchronized BluetoothDevice getDevice() { 151 return mDevice; 152 } 153 154 private void onConnectionStateChanged(int prevState, int state) { 155 // mDevice == null only at setInitialState 156 if (mDevice == null) return; 157 if (DBG) Log.d(TAG, "Connection state " + mDevice + ": " + prevState + "->" + state); 158 Intent intent = new Intent(BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED); 159 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 160 intent.putExtra(BluetoothProfile.EXTRA_STATE, state); 161 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 162 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 163 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 164 } 165 166 public synchronized int getState() { 167 IState currentState = this.getCurrentState(); 168 if (currentState.getClass() == Disconnected.class) { 169 return BluetoothProfile.STATE_DISCONNECTED; 170 } 171 if (currentState.getClass() == Connected.class) { 172 return BluetoothProfile.STATE_CONNECTED; 173 } 174 if (currentState.getClass() == Connecting.class) { 175 return BluetoothProfile.STATE_CONNECTING; 176 } 177 if (currentState.getClass() == Disconnecting.class) { 178 return BluetoothProfile.STATE_DISCONNECTING; 179 } 180 return BluetoothProfile.STATE_DISCONNECTED; 181 } 182 183 public boolean connect(BluetoothDevice device) { 184 if (DBG) Log.d(TAG, "Connect Request " + device.getAddress()); 185 sendMessage(MSG_CONNECT, device); 186 return true; 187 } 188 189 public boolean disconnect(BluetoothDevice device) { 190 if (DBG) Log.d(TAG, "Disconnect Request " + device.getAddress()); 191 sendMessage(MSG_DISCONNECT, device); 192 return true; 193 } 194 195 public synchronized boolean sendMapMessage(Uri[] contacts, String message, 196 PendingIntent sentIntent, 197 PendingIntent deliveredIntent) { 198 if (DBG) Log.d(TAG, "Send Message " + message); 199 if (contacts == null || contacts.length <= 0) return false; 200 if (this.getCurrentState() == mConnected) { 201 Bmessage bmsg = new Bmessage(); 202 // Set type and status. 203 bmsg.setType(getDefaultMessageType()); 204 bmsg.setStatus(Bmessage.Status.READ); 205 206 for (Uri contact : contacts) { 207 // Who to send the message to. 208 VCardEntry dest_entry = new VCardEntry(); 209 VCardProperty dest_entry_phone = new VCardProperty(); 210 if (DBG) Log.d(TAG, "Scheme " + contact.getScheme()); 211 if (PhoneAccount.SCHEME_TEL.equals(contact.getScheme())) { 212 dest_entry_phone.setName(VCardConstants.PROPERTY_TEL); 213 dest_entry_phone.addValues(contact.getSchemeSpecificPart()); 214 if (DBG) { 215 Log.d(TAG, 216 "Sending to phone numbers " + dest_entry_phone.getValueList()); 217 } 218 } else { 219 if (DBG) Log.w(TAG, "Scheme " + contact.getScheme() + " not supported."); 220 return false; 221 } 222 dest_entry.addProperty(dest_entry_phone); 223 bmsg.addRecipient(dest_entry); 224 } 225 226 // Message of the body. 227 bmsg.setBodyContent(message); 228 if (sentIntent != null) { 229 sentReceiptRequested.put(bmsg, sentIntent); 230 } 231 if (deliveredIntent != null) { 232 deliveryReceiptRequested.put(bmsg, deliveredIntent); 233 } 234 sendMessage(MSG_OUTBOUND_MESSAGE, bmsg); 235 return true; 236 } 237 return false; 238 } 239 240 synchronized boolean getMessage(String handle) { 241 if (DBG) Log.d(TAG, "getMessage" + handle); 242 if (this.getCurrentState() == mConnected) { 243 sendMessage(MSG_INBOUND_MESSAGE, handle); 244 return true; 245 } 246 return false; 247 } 248 249 synchronized boolean getUnreadMessages() { 250 if (DBG) Log.d(TAG, "getMessage"); 251 if (this.getCurrentState() == mConnected) { 252 sendMessage(MSG_GET_MESSAGE_LISTING, FOLDER_INBOX); 253 return true; 254 } 255 return false; 256 } 257 258 private String getContactURIFromPhone(String number) { 259 return PhoneAccount.SCHEME_TEL + ":" + number; 260 } 261 262 Bmessage.Type getDefaultMessageType() { 263 synchronized (mDefaultMessageType) { 264 return mDefaultMessageType; 265 } 266 } 267 268 void setDefaultMessageType(SdpMasRecord sdpMasRecord) { 269 int supportedMessageTypes = sdpMasRecord.getSupportedMessageTypes(); 270 synchronized (mDefaultMessageType) { 271 if ((supportedMessageTypes & SdpMasRecord.MessageType.SMS_CDMA) > 0) { 272 mDefaultMessageType = Bmessage.Type.SMS_CDMA; 273 } else if ((supportedMessageTypes & SdpMasRecord.MessageType.SMS_GSM) > 0) { 274 mDefaultMessageType = Bmessage.Type.SMS_GSM; 275 } 276 } 277 } 278 279 class Disconnected extends State { 280 @Override 281 public void enter() { 282 if (DBG) Log.d(TAG, "Enter Disconnected: " + getCurrentMessage().what); 283 onConnectionStateChanged(mPreviousState, 284 BluetoothProfile.STATE_DISCONNECTED); 285 mPreviousState = BluetoothProfile.STATE_DISCONNECTED; 286 } 287 288 @Override 289 public boolean processMessage(Message message) { 290 switch (message.what) { 291 case MSG_CONNECT: 292 synchronized (MceStateMachine.this) { 293 mDevice = (BluetoothDevice) message.obj; 294 } 295 transitionTo(mConnecting); 296 break; 297 298 default: 299 Log.w(TAG, "Unexpected message: " + message.what + " from state:" + 300 this.getName()); 301 return NOT_HANDLED; 302 } 303 return HANDLED; 304 } 305 306 @Override 307 public void exit() { 308 mPreviousState = BluetoothProfile.STATE_DISCONNECTED; 309 } 310 } 311 312 class Connecting extends State { 313 @Override 314 public void enter() { 315 if (DBG) Log.d(TAG, "Enter Connecting: " + getCurrentMessage().what); 316 onConnectionStateChanged(mPreviousState, 317 BluetoothProfile.STATE_CONNECTING); 318 319 IntentFilter filter = new IntentFilter(); 320 filter.addAction(BluetoothDevice.ACTION_SDP_RECORD); 321 filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); 322 // unregisterReceiver in Disconnecting 323 mService.registerReceiver(mMapReceiver, filter); 324 325 BluetoothAdapter.getDefaultAdapter().cancelDiscovery(); 326 // When commanded to connect begin SDP to find the MAS server. 327 mDevice.sdpSearch(BluetoothUuid.MAS); 328 sendMessageDelayed(MSG_CONNECTING_TIMEOUT, TIMEOUT); 329 } 330 331 @Override 332 public boolean processMessage(Message message) { 333 if (DBG) Log.d(TAG, "processMessage" + this.getName() + message.what); 334 335 switch (message.what) { 336 case MSG_MAS_SDP_DONE: 337 if (DBG) Log.d(TAG, "SDP Complete"); 338 if (mMasClient == null) { 339 mMasClient = new MasClient(mDevice, 340 MceStateMachine.this, 341 (SdpMasRecord) message.obj); 342 setDefaultMessageType((SdpMasRecord) message.obj); 343 } 344 break; 345 346 case MSG_MAS_CONNECTED: 347 transitionTo(mConnected); 348 break; 349 350 case MSG_CONNECTING_TIMEOUT: 351 transitionTo(mDisconnecting); 352 break; 353 354 case MSG_CONNECT: 355 case MSG_DISCONNECT: 356 deferMessage(message); 357 break; 358 359 default: 360 Log.w(TAG, "Unexpected message: " + message.what + " from state:" + 361 this.getName()); 362 return NOT_HANDLED; 363 } 364 return HANDLED; 365 } 366 367 @Override 368 public void exit() { 369 mPreviousState = BluetoothProfile.STATE_CONNECTING; 370 removeMessages(MSG_CONNECTING_TIMEOUT); 371 } 372 } 373 374 class Connected extends State { 375 @Override 376 public void enter() { 377 if (DBG) Log.d(TAG, "Enter Connected: " + getCurrentMessage().what); 378 onConnectionStateChanged(mPreviousState, 379 BluetoothProfile.STATE_CONNECTED); 380 381 mMasClient.makeRequest(new RequestSetPath(FOLDER_TELECOM)); 382 mMasClient.makeRequest(new RequestSetPath(FOLDER_MSG)); 383 mMasClient.makeRequest(new RequestSetPath(FOLDER_INBOX)); 384 mMasClient.makeRequest(new RequestGetFolderListing(0, 0)); 385 mMasClient.makeRequest(new RequestSetPath(false)); 386 mMasClient.makeRequest(new RequestSetNotificationRegistration(true)); 387 } 388 389 @Override 390 public boolean processMessage(Message message) { 391 switch (message.what) { 392 case MSG_DISCONNECT: 393 if (mDevice.equals(message.obj)) { 394 transitionTo(mDisconnecting); 395 } 396 break; 397 398 case MSG_OUTBOUND_MESSAGE: 399 mMasClient.makeRequest(new RequestPushMessage(FOLDER_OUTBOX, 400 (Bmessage) message.obj, null, false, false)); 401 break; 402 403 case MSG_INBOUND_MESSAGE: 404 mMasClient.makeRequest(new RequestGetMessage((String) message.obj, 405 MasClient.CharsetType.UTF_8, false)); 406 break; 407 408 case MSG_NOTIFICATION: 409 processNotification(message); 410 break; 411 412 case MSG_GET_LISTING: 413 mMasClient.makeRequest(new RequestGetFolderListing(0, 0)); 414 break; 415 416 case MSG_GET_MESSAGE_LISTING: 417 MessagesFilter filter = new MessagesFilter(); 418 filter.setMessageType((byte) 0); 419 mMasClient.makeRequest( 420 new RequestGetMessagesListing((String) message.obj, 0, 421 filter, 0, 1, 0)); 422 break; 423 424 case MSG_MAS_REQUEST_COMPLETED: 425 if (DBG) Log.d(TAG, "Completed request"); 426 if (message.obj instanceof RequestGetMessage) { 427 processInboundMessage((RequestGetMessage) message.obj); 428 } else if (message.obj instanceof RequestPushMessage) { 429 String messageHandle = 430 ((RequestPushMessage) message.obj).getMsgHandle(); 431 if (DBG) Log.d(TAG, "Message Sent......." + messageHandle); 432 sentMessageLog.put(messageHandle, 433 ((RequestPushMessage) message.obj).getBMsg()); 434 } else if (message.obj instanceof RequestGetMessagesListing) { 435 processMessageListing((RequestGetMessagesListing) message.obj); 436 } 437 break; 438 439 case MSG_CONNECT: 440 if (!mDevice.equals(message.obj)) { 441 deferMessage(message); 442 transitionTo(mDisconnecting); 443 } 444 break; 445 446 default: 447 Log.w(TAG, "Unexpected message: " + message.what + " from state:" + 448 this.getName()); 449 return NOT_HANDLED; 450 } 451 return HANDLED; 452 } 453 454 @Override 455 public void exit() { 456 mPreviousState = BluetoothProfile.STATE_CONNECTED; 457 } 458 459 private void processNotification(Message msg) { 460 if (DBG) Log.d(TAG, "Handler: msg: " + msg.what); 461 462 switch (msg.what) { 463 case MSG_NOTIFICATION: 464 EventReport ev = (EventReport) msg.obj; 465 if (DBG) Log.d(TAG, "Message Type = " + ev.getType()); 466 if (DBG) Log.d(TAG, "Message handle = " + ev.getHandle()); 467 switch (ev.getType()) { 468 469 case NEW_MESSAGE: 470 //mService.get().sendNewMessageNotification(ev); 471 mMasClient.makeRequest(new RequestGetMessage(ev.getHandle(), 472 MasClient.CharsetType.UTF_8, false)); 473 break; 474 475 case DELIVERY_SUCCESS: 476 case SENDING_SUCCESS: 477 notifySentMessageStatus(ev.getHandle(), ev.getType()); 478 break; 479 } 480 } 481 } 482 483 private void processMessageListing(RequestGetMessagesListing request) { 484 if (DBG) Log.d(TAG, "processMessageListing"); 485 ArrayList<com.android.bluetooth.mapclient.Message> messageHandles = request.getList(); 486 if (messageHandles != null) { 487 for (com.android.bluetooth.mapclient.Message handle : messageHandles) { 488 if (DBG) Log.d(TAG, "getting message "); 489 getMessage(handle.getHandle()); 490 } 491 } 492 } 493 494 private void processInboundMessage(RequestGetMessage request) { 495 Bmessage message = request.getMessage(); 496 if (DBG) Log.d(TAG, "Notify inbound Message" + message); 497 498 if (message == null) return; 499 if (!INBOX_PATH.equalsIgnoreCase(message.getFolder())) { 500 if (DBG) Log.d(TAG, "Ignoring message received in " + message.getFolder() + "."); 501 return; 502 } 503 switch (message.getType()) { 504 case SMS_CDMA: 505 case SMS_GSM: 506 if (DBG) Log.d(TAG, "Body: " + message.getBodyContent()); 507 if (DBG) Log.d(TAG, message.toString()); 508 if (DBG) Log.d(TAG, "Recipients" + message.getRecipients().toString()); 509 510 Intent intent = new Intent(); 511 intent.setAction(BluetoothMapClient.ACTION_MESSAGE_RECEIVED); 512 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 513 intent.putExtra(BluetoothMapClient.EXTRA_MESSAGE_HANDLE, request.getHandle()); 514 intent.putExtra(android.content.Intent.EXTRA_TEXT, 515 message.getBodyContent()); 516 VCardEntry originator = message.getOriginator(); 517 if (originator != null) { 518 if (DBG) Log.d(TAG, originator.toString()); 519 List<VCardEntry.PhoneData> phoneData = originator.getPhoneList(); 520 if (phoneData != null && phoneData.size() > 0) { 521 String phoneNumber = phoneData.get(0).getNumber(); 522 if (DBG) { 523 Log.d(TAG, "Originator number: " + phoneNumber); 524 } 525 intent.putExtra(BluetoothMapClient.EXTRA_SENDER_CONTACT_URI, 526 getContactURIFromPhone(phoneNumber)); 527 } 528 intent.putExtra(BluetoothMapClient.EXTRA_SENDER_CONTACT_NAME, 529 originator.getDisplayName()); 530 } 531 mService.sendBroadcast(intent); 532 break; 533 534 case MMS: 535 case EMAIL: 536 default: 537 Log.e(TAG, "Received unhandled type" + message.getType().toString()); 538 break; 539 } 540 } 541 542 private void notifySentMessageStatus(String handle, EventReport.Type status) { 543 if (DBG) Log.d(TAG, "got a status for " + handle + " Status = " + status); 544 PendingIntent intentToSend = null; 545 if (status == EventReport.Type.SENDING_SUCCESS) { 546 intentToSend = sentReceiptRequested.remove(sentMessageLog.get(handle)); 547 } else if (status == EventReport.Type.DELIVERY_SUCCESS) { 548 intentToSend = deliveryReceiptRequested.remove(sentMessageLog.get(handle)); 549 } 550 551 if (intentToSend != null) { 552 try { 553 if (DBG) Log.d(TAG, "*******Sending " + intentToSend); 554 intentToSend.send(); 555 } catch (PendingIntent.CanceledException e) { 556 Log.w(TAG, "Notification Request Canceled" + e); 557 } 558 } 559 } 560 } 561 562 class Disconnecting extends State { 563 @Override 564 public void enter() { 565 if (DBG) Log.d(TAG, "Enter Disconnecting: " + getCurrentMessage().what); 566 onConnectionStateChanged(mPreviousState, 567 BluetoothProfile.STATE_DISCONNECTING); 568 mService.unregisterReceiver(mMapReceiver); 569 570 if (mMasClient != null) { 571 mMasClient.makeRequest(new RequestSetNotificationRegistration(false)); 572 mMasClient.shutdown(); 573 sendMessageDelayed(MSG_DISCONNECTING_TIMEOUT, TIMEOUT); 574 } else { 575 // MAP was never connected 576 transitionTo(mDisconnected); 577 } 578 } 579 580 @Override 581 public boolean processMessage(Message message) { 582 switch (message.what) { 583 case MSG_DISCONNECTING_TIMEOUT: 584 case MSG_MAS_DISCONNECTED: 585 mMasClient = null; 586 transitionTo(mDisconnected); 587 break; 588 589 case MSG_CONNECT: 590 case MSG_DISCONNECT: 591 deferMessage(message); 592 break; 593 594 default: 595 Log.w(TAG, "Unexpected message: " + message.what + " from state:" + 596 this.getName()); 597 return NOT_HANDLED; 598 } 599 return HANDLED; 600 } 601 602 @Override 603 public void exit() { 604 mPreviousState = BluetoothProfile.STATE_DISCONNECTING; 605 removeMessages(MSG_DISCONNECTING_TIMEOUT); 606 } 607 } 608 609 void receiveEvent(EventReport ev) { 610 if (DBG) Log.d(TAG, "Message Type = " + ev.getType()); 611 if (DBG) Log.d(TAG, "Message handle = " + ev.getHandle()); 612 sendMessage(MSG_NOTIFICATION, ev); 613 } 614 615 private class MapBroadcastReceiver extends BroadcastReceiver { 616 @Override 617 public void onReceive(Context context, Intent intent) { 618 if (DBG) Log.d(TAG, "onReceive"); 619 String action = intent.getAction(); 620 if (DBG) Log.d(TAG, "onReceive: " + action); 621 if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { 622 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 623 if (getDevice().equals(device) && getState() == BluetoothProfile.STATE_CONNECTED) { 624 disconnect(device); 625 } 626 } 627 628 if (BluetoothDevice.ACTION_SDP_RECORD.equals(intent.getAction())) { 629 ParcelUuid uuid = intent.getParcelableExtra(BluetoothDevice.EXTRA_UUID); 630 if (DBG) Log.d(TAG, "UUID of SDP: " + uuid); 631 632 if (uuid.equals(BluetoothUuid.MAS)) { 633 // Check if we have a valid SDP record. 634 SdpMasRecord masRecord = 635 intent.getParcelableExtra(BluetoothDevice.EXTRA_SDP_RECORD); 636 if (DBG) Log.d(TAG, "SDP = " + masRecord); 637 int status = intent.getIntExtra(BluetoothDevice.EXTRA_SDP_SEARCH_STATUS, -1); 638 if (masRecord == null) { 639 Log.w(TAG, "SDP search ended with no MAS record. Status: " + status); 640 return; 641 } 642 obtainMessage( 643 MceStateMachine.MSG_MAS_SDP_DONE, 644 masRecord).sendToTarget(); 645 } 646 } 647 } 648 } 649 } 650