MceStateMachine.java revision 564b35e8f2ef5dd0015a49f1e52ea94dc0a42068
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.PendingIntent;
44import android.bluetooth.BluetoothAdapter;
45import android.bluetooth.BluetoothDevice;
46import android.bluetooth.BluetoothMapClient;
47import android.bluetooth.BluetoothProfile;
48import android.bluetooth.BluetoothUuid;
49import android.bluetooth.SdpMasRecord;
50import android.content.BroadcastReceiver;
51import android.content.Context;
52import android.content.Intent;
53import android.content.IntentFilter;
54import android.net.Uri;
55import android.os.Message;
56import android.os.ParcelUuid;
57import android.provider.ContactsContract;
58import android.telecom.PhoneAccount;
59import android.util.Log;
60
61import com.android.bluetooth.btservice.ProfileService;
62import com.android.internal.util.IState;
63import com.android.internal.util.State;
64import com.android.internal.util.StateMachine;
65import com.android.vcard.VCardConstants;
66import com.android.vcard.VCardEntry;
67import com.android.vcard.VCardProperty;
68
69import java.util.ArrayList;
70import java.util.HashMap;
71import 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 */
78final 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(android.content.Intent.EXTRA_TEXT,
514                            message.getBodyContent());
515                    VCardEntry originator = message.getOriginator();
516                    if (originator != null) {
517                        if (DBG) Log.d(TAG, originator.toString());
518                        List<VCardEntry.PhoneData> phoneData = originator.getPhoneList();
519                        if (phoneData != null && phoneData.size() > 0) {
520                            String phoneNumber = phoneData.get(0).getNumber();
521                            if (DBG) {
522                                Log.d(TAG, "Originator number: " + phoneNumber);
523                            }
524                            intent.putExtra(BluetoothMapClient.EXTRA_SENDER_CONTACT_URI,
525                                    new String[] {getContactURIFromPhone(phoneNumber)});
526                        }
527                        intent.putExtra(BluetoothMapClient.EXTRA_SENDER_CONTACT_NAME,
528                                new String[] {originator.getDisplayName()});
529                    }
530                    mService.sendBroadcast(intent);
531                    break;
532
533                case MMS:
534                case EMAIL:
535                default:
536                    Log.e(TAG, "Received unhandled type" + message.getType().toString());
537                    break;
538            }
539        }
540
541        private void notifySentMessageStatus(String handle, EventReport.Type status) {
542            if (DBG) Log.d(TAG, "got a status for " + handle + " Status = " + status);
543            PendingIntent intentToSend = null;
544            if (status == EventReport.Type.SENDING_SUCCESS) {
545                intentToSend = sentReceiptRequested.remove(sentMessageLog.get(handle));
546            } else if (status == EventReport.Type.DELIVERY_SUCCESS) {
547                intentToSend = deliveryReceiptRequested.remove(sentMessageLog.get(handle));
548            }
549
550            if (intentToSend != null) {
551                try {
552                    if (DBG) Log.d(TAG, "*******Sending " + intentToSend);
553                    intentToSend.send();
554                } catch (PendingIntent.CanceledException e) {
555                    Log.w(TAG, "Notification Request Canceled" + e);
556                }
557            }
558        }
559    }
560
561    class Disconnecting extends State {
562        @Override
563        public void enter() {
564            if (DBG) Log.d(TAG, "Enter Disconnecting: " + getCurrentMessage().what);
565            onConnectionStateChanged(mPreviousState,
566                    BluetoothProfile.STATE_DISCONNECTING);
567            mService.unregisterReceiver(mMapReceiver);
568
569            if (mMasClient != null) {
570                mMasClient.makeRequest(new RequestSetNotificationRegistration(false));
571                mMasClient.shutdown();
572                sendMessageDelayed(MSG_DISCONNECTING_TIMEOUT, TIMEOUT);
573            } else {
574                // MAP was never connected
575                transitionTo(mDisconnected);
576            }
577        }
578
579        @Override
580        public boolean processMessage(Message message) {
581            switch (message.what) {
582                case MSG_DISCONNECTING_TIMEOUT:
583                case MSG_MAS_DISCONNECTED:
584                    mMasClient = null;
585                    transitionTo(mDisconnected);
586                    break;
587
588                case MSG_CONNECT:
589                case MSG_DISCONNECT:
590                    deferMessage(message);
591                    break;
592
593                default:
594                    Log.w(TAG, "Unexpected message: " + message.what + " from state:" +
595                        this.getName());
596                    return NOT_HANDLED;
597            }
598            return HANDLED;
599        }
600
601        @Override
602        public void exit() {
603            mPreviousState = BluetoothProfile.STATE_DISCONNECTING;
604            removeMessages(MSG_DISCONNECTING_TIMEOUT);
605        }
606    }
607
608    void receiveEvent(EventReport ev) {
609        if (DBG) Log.d(TAG, "Message Type = " + ev.getType());
610        if (DBG) Log.d(TAG, "Message handle = " + ev.getHandle());
611        sendMessage(MSG_NOTIFICATION, ev);
612    }
613
614    private class MapBroadcastReceiver extends BroadcastReceiver {
615        @Override
616        public void onReceive(Context context, Intent intent) {
617            if (DBG) Log.d(TAG, "onReceive");
618            String action = intent.getAction();
619            if (DBG) Log.d(TAG, "onReceive: " + action);
620            if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
621                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
622                if (getDevice().equals(device) && getState() == BluetoothProfile.STATE_CONNECTED) {
623                    disconnect(device);
624                }
625            }
626
627            if (BluetoothDevice.ACTION_SDP_RECORD.equals(intent.getAction())) {
628                ParcelUuid uuid = intent.getParcelableExtra(BluetoothDevice.EXTRA_UUID);
629                if (DBG) Log.d(TAG, "UUID of SDP: " + uuid);
630
631                if (uuid.equals(BluetoothUuid.MAS)) {
632                    // Check if we have a valid SDP record.
633                    SdpMasRecord masRecord =
634                            intent.getParcelableExtra(BluetoothDevice.EXTRA_SDP_RECORD);
635                    if (DBG) Log.d(TAG, "SDP = " + masRecord);
636                    int status = intent.getIntExtra(BluetoothDevice.EXTRA_SDP_SEARCH_STATUS, -1);
637                    if (masRecord == null) {
638                        Log.w(TAG, "SDP search ended with no MAS record. Status: " + status);
639                        return;
640                    }
641                    obtainMessage(
642                            MceStateMachine.MSG_MAS_SDP_DONE,
643                            masRecord).sendToTarget();
644                }
645            }
646        }
647    }
648}
649