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