LocalBluetoothProfileManager.java revision 8c04b90cc95b16f8d7e99a61d8e2c940ed83b0c4
1/*
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 com.android.settings.bluetooth;
18
19import com.android.settings.R;
20
21import android.bluetooth.BluetoothA2dp;
22import android.bluetooth.BluetoothAdapter;
23import android.bluetooth.BluetoothDevice;
24import android.bluetooth.BluetoothHeadset;
25import android.bluetooth.BluetoothInputDevice;
26import android.bluetooth.BluetoothPan;
27import android.bluetooth.BluetoothProfile;
28import android.bluetooth.BluetoothUuid;
29import android.os.Handler;
30import android.os.ParcelUuid;
31import android.util.Log;
32
33import java.util.HashMap;
34import java.util.Iterator;
35import java.util.LinkedList;
36import java.util.List;
37import java.util.Map;
38
39/**
40 * LocalBluetoothProfileManager is an abstract class defining the basic
41 * functionality related to a profile.
42 */
43public abstract class LocalBluetoothProfileManager {
44    private static final String TAG = "LocalBluetoothProfileManager";
45
46    /* package */ static final ParcelUuid[] HEADSET_PROFILE_UUIDS = new ParcelUuid[] {
47        BluetoothUuid.HSP,
48        BluetoothUuid.Handsfree,
49    };
50
51    /* package */ static final ParcelUuid[] A2DP_PROFILE_UUIDS = new ParcelUuid[] {
52        BluetoothUuid.AudioSink,
53        BluetoothUuid.AdvAudioDist,
54    };
55
56    /* package */ static final ParcelUuid[] OPP_PROFILE_UUIDS = new ParcelUuid[] {
57        BluetoothUuid.ObexObjectPush
58    };
59
60    /* package */ static final ParcelUuid[] HID_PROFILE_UUIDS = new ParcelUuid[] {
61        BluetoothUuid.Hid
62    };
63
64    /* package */ static final ParcelUuid[] PANU_PROFILE_UUIDS = new ParcelUuid[] {
65        BluetoothUuid.PANU
66    };
67
68    /* package */ static final ParcelUuid[] NAP_PROFILE_UUIDS = new ParcelUuid[] {
69        BluetoothUuid.NAP
70    };
71
72    /**
73     * An interface for notifying BluetoothHeadset IPC clients when they have
74     * been connected to the BluetoothHeadset service.
75     */
76    public interface ServiceListener {
77        /**
78         * Called to notify the client when this proxy object has been
79         * connected to the BluetoothHeadset service. Clients must wait for
80         * this callback before making IPC calls on the BluetoothHeadset
81         * service.
82         */
83        public void onServiceConnected();
84
85        /**
86         * Called to notify the client that this proxy object has been
87         * disconnected from the BluetoothHeadset service. Clients must not
88         * make IPC calls on the BluetoothHeadset service after this callback.
89         * This callback will currently only occur if the application hosting
90         * the BluetoothHeadset service, but may be called more often in future.
91         */
92        public void onServiceDisconnected();
93    }
94
95    // TODO: close profiles when we're shutting down
96    private static Map<Profile, LocalBluetoothProfileManager> sProfileMap =
97            new HashMap<Profile, LocalBluetoothProfileManager>();
98
99    protected LocalBluetoothManager mLocalManager;
100
101    public static void init(LocalBluetoothManager localManager) {
102        synchronized (sProfileMap) {
103            if (sProfileMap.size() == 0) {
104                LocalBluetoothProfileManager profileManager;
105
106                profileManager = new A2dpProfileManager(localManager);
107                sProfileMap.put(Profile.A2DP, profileManager);
108
109                profileManager = new HeadsetProfileManager(localManager);
110                sProfileMap.put(Profile.HEADSET, profileManager);
111
112                profileManager = new OppProfileManager(localManager);
113                sProfileMap.put(Profile.OPP, profileManager);
114
115                profileManager = new HidProfileManager(localManager);
116                sProfileMap.put(Profile.HID, profileManager);
117
118                profileManager = new PanProfileManager(localManager);
119                sProfileMap.put(Profile.PAN, profileManager);
120            }
121        }
122    }
123
124    // TODO(): Combine the init and updateLocalProfiles codes.
125    // init can get called from various paths, it makes no sense to add and then delete.
126    public static void updateLocalProfiles(LocalBluetoothManager localManager, ParcelUuid[] uuids) {
127        if (!BluetoothUuid.containsAnyUuid(uuids, HEADSET_PROFILE_UUIDS)) {
128            sProfileMap.remove(Profile.HEADSET);
129        }
130
131        if (BluetoothUuid.containsAnyUuid(uuids, A2DP_PROFILE_UUIDS)) {
132            sProfileMap.remove(Profile.A2DP);
133        }
134
135        if (BluetoothUuid.containsAnyUuid(uuids, OPP_PROFILE_UUIDS)) {
136            sProfileMap.remove(Profile.OPP);
137        }
138
139        if (BluetoothUuid.containsAnyUuid(uuids, HID_PROFILE_UUIDS)) {
140            sProfileMap.remove(Profile.HID);
141        }
142
143        if (BluetoothUuid.containsAnyUuid(uuids, PANU_PROFILE_UUIDS)) {
144            sProfileMap.remove(Profile.PAN);
145        }
146    }
147
148    private static LinkedList<ServiceListener> mServiceListeners =
149            new LinkedList<ServiceListener>();
150
151    public static void addServiceListener(ServiceListener l) {
152        mServiceListeners.add(l);
153    }
154
155    public static void removeServiceListener(ServiceListener l) {
156        mServiceListeners.remove(l);
157    }
158
159    public static boolean isManagerReady() {
160        // Getting just the headset profile is fine for now. Will need to deal with A2DP
161        // and others if they aren't always in a ready state.
162        LocalBluetoothProfileManager profileManager = sProfileMap.get(Profile.HEADSET);
163        if (profileManager == null) {
164            return sProfileMap.size() > 0;
165        }
166        return profileManager.isProfileReady();
167    }
168
169    public static LocalBluetoothProfileManager getProfileManager(LocalBluetoothManager localManager,
170            Profile profile) {
171        // Note: This code assumes that "localManager" is same as the
172        // LocalBluetoothManager that was used to initialize the sProfileMap.
173        // If that every changes, we can't just keep one copy of sProfileMap.
174        synchronized (sProfileMap) {
175            LocalBluetoothProfileManager profileManager = sProfileMap.get(profile);
176            if (profileManager == null) {
177                Log.e(TAG, "profileManager can't be found for " + profile.toString());
178            }
179            return profileManager;
180        }
181    }
182
183    /**
184     * Temporary method to fill profiles based on a device's class.
185     *
186     * NOTE: This list happens to define the connection order. We should put this logic in a more
187     * well known place when this method is no longer temporary.
188     * @param uuids of the remote device
189     * @param profiles The list of profiles to fill
190     */
191    public static void updateProfiles(ParcelUuid[] uuids, List<Profile> profiles) {
192        profiles.clear();
193
194        if (uuids == null) {
195            return;
196        }
197
198        if (BluetoothUuid.containsAnyUuid(uuids, HEADSET_PROFILE_UUIDS) &&
199            sProfileMap.containsKey(Profile.HEADSET)) {
200            profiles.add(Profile.HEADSET);
201        }
202
203        if (BluetoothUuid.containsAnyUuid(uuids, A2DP_PROFILE_UUIDS) &&
204            sProfileMap.containsKey(Profile.A2DP)) {
205            profiles.add(Profile.A2DP);
206        }
207
208        if (BluetoothUuid.containsAnyUuid(uuids, OPP_PROFILE_UUIDS) &&
209            sProfileMap.containsKey(Profile.OPP)) {
210            profiles.add(Profile.OPP);
211        }
212
213        if (BluetoothUuid.containsAnyUuid(uuids, HID_PROFILE_UUIDS) &&
214            sProfileMap.containsKey(Profile.HID)) {
215            profiles.add(Profile.HID);
216        }
217
218        if (BluetoothUuid.containsAnyUuid(uuids, PANU_PROFILE_UUIDS) &&
219            sProfileMap.containsKey(Profile.PAN)) {
220            profiles.add(Profile.PAN);
221        }
222    }
223
224    protected LocalBluetoothProfileManager(LocalBluetoothManager localManager) {
225        mLocalManager = localManager;
226    }
227
228    public abstract List<BluetoothDevice> getConnectedDevices();
229
230    public abstract boolean connect(BluetoothDevice device);
231
232    public abstract boolean disconnect(BluetoothDevice device);
233
234    public abstract int getConnectionStatus(BluetoothDevice device);
235
236    public abstract int getSummary(BluetoothDevice device);
237
238    public abstract int convertState(int a2dpState);
239
240    public abstract boolean isPreferred(BluetoothDevice device);
241
242    public abstract int getPreferred(BluetoothDevice device);
243
244    public abstract void setPreferred(BluetoothDevice device, boolean preferred);
245
246    public boolean isConnected(BluetoothDevice device) {
247        return SettingsBtStatus.isConnectionStatusConnected(getConnectionStatus(device));
248    }
249
250    public abstract boolean isProfileReady();
251
252    public int getDrawableResource() {
253        return R.drawable.ic_bt_headphones_a2dp;
254    }
255
256    // TODO: int instead of enum
257    public static enum Profile {
258        HEADSET(R.string.bluetooth_profile_headset),
259        A2DP(R.string.bluetooth_profile_a2dp),
260        OPP(R.string.bluetooth_profile_opp),
261        HID(R.string.bluetooth_profile_hid),
262        PAN(R.string.bluetooth_profile_pan);
263
264        public final int localizedString;
265
266        private Profile(int localizedString) {
267            this.localizedString = localizedString;
268        }
269    }
270
271    /**
272     * A2dpProfileManager is an abstraction for the {@link BluetoothA2dp} service.
273     */
274    private static class A2dpProfileManager extends LocalBluetoothProfileManager
275          implements BluetoothProfile.ServiceListener {
276        private BluetoothA2dp mService;
277
278        // TODO(): The calls must wait for mService. Its not null just
279        // because it runs in the system server.
280        public A2dpProfileManager(LocalBluetoothManager localManager) {
281            super(localManager);
282            BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
283            adapter.getProfileProxy(localManager.getContext(), this, BluetoothProfile.A2DP);
284
285        }
286
287        public void onServiceConnected(int profile, BluetoothProfile proxy) {
288            mService = (BluetoothA2dp) proxy;
289        }
290
291        public void onServiceDisconnected(int profile) {
292            mService = null;
293        }
294
295        @Override
296        public List<BluetoothDevice> getConnectedDevices() {
297            return mService.getDevicesMatchingConnectionStates(
298                  new int[] {BluetoothProfile.STATE_CONNECTED,
299                             BluetoothProfile.STATE_CONNECTING,
300                             BluetoothProfile.STATE_DISCONNECTING});
301        }
302
303        @Override
304        public boolean connect(BluetoothDevice device) {
305            List<BluetoothDevice> sinks = getConnectedDevices();
306            if (sinks != null) {
307                for (BluetoothDevice sink : sinks) {
308                    mService.disconnect(sink);
309                }
310            }
311            return mService.connect(device);
312        }
313
314        @Override
315        public boolean disconnect(BluetoothDevice device) {
316            // Downgrade priority as user is disconnecting the sink.
317            if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
318                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
319            }
320            return mService.disconnect(device);
321        }
322
323        @Override
324        public int getConnectionStatus(BluetoothDevice device) {
325            return convertState(mService.getConnectionState(device));
326        }
327
328        @Override
329        public int getSummary(BluetoothDevice device) {
330            int connectionStatus = getConnectionStatus(device);
331
332            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
333                return R.string.bluetooth_a2dp_profile_summary_connected;
334            } else {
335                return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
336            }
337        }
338
339        @Override
340        public boolean isPreferred(BluetoothDevice device) {
341            return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
342        }
343
344        @Override
345        public int getPreferred(BluetoothDevice device) {
346            return mService.getPriority(device);
347        }
348
349        @Override
350        public void setPreferred(BluetoothDevice device, boolean preferred) {
351            if (preferred) {
352                if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
353                    mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
354                }
355            } else {
356                mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
357            }
358        }
359
360        @Override
361        public int convertState(int a2dpState) {
362            switch (a2dpState) {
363            case BluetoothProfile.STATE_CONNECTED:
364                return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
365            case BluetoothProfile.STATE_CONNECTING:
366                return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
367            case BluetoothProfile.STATE_DISCONNECTED:
368                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
369            case BluetoothProfile.STATE_DISCONNECTING:
370                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING;
371            case BluetoothA2dp.STATE_PLAYING:
372                return SettingsBtStatus.CONNECTION_STATUS_ACTIVE;
373            default:
374                return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
375            }
376        }
377
378        @Override
379        public boolean isProfileReady() {
380            return true;
381        }
382
383        @Override
384        public int getDrawableResource() {
385            return R.drawable.ic_bt_headphones_a2dp;
386        }
387    }
388
389    /**
390     * HeadsetProfileManager is an abstraction for the {@link BluetoothHeadset} service.
391     */
392    private static class HeadsetProfileManager extends LocalBluetoothProfileManager
393            implements BluetoothProfile.ServiceListener {
394        private BluetoothHeadset mService;
395        private Handler mUiHandler = new Handler();
396        private boolean profileReady = false;
397
398        // TODO(): The calls must get queued if mService becomes null.
399        // It can happen  when phone app crashes for some reason.
400        // All callers should have service listeners. Dock Service is the only
401        // one right now.
402        public HeadsetProfileManager(LocalBluetoothManager localManager) {
403            super(localManager);
404            BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
405            adapter.getProfileProxy(localManager.getContext(), this, BluetoothProfile.HEADSET);
406        }
407
408        public void onServiceConnected(int profile, BluetoothProfile proxy) {
409            mService = (BluetoothHeadset) proxy;
410            profileReady = true;
411            // This could be called on a non-UI thread, funnel to UI thread.
412            mUiHandler.post(new Runnable() {
413                public void run() {
414                    /*
415                     * We just bound to the service, so refresh the UI of the
416                     * headset device.
417                     */
418                    List<BluetoothDevice> deviceList = mService.getConnectedDevices();
419                    if (deviceList.size() == 0) return;
420
421                    mLocalManager.getCachedDeviceManager()
422                            .onProfileStateChanged(deviceList.get(0), Profile.HEADSET,
423                                                   BluetoothProfile.STATE_CONNECTED);
424                }
425            });
426
427            if (mServiceListeners.size() > 0) {
428                Iterator<ServiceListener> it = mServiceListeners.iterator();
429                while(it.hasNext()) {
430                    it.next().onServiceConnected();
431                }
432            }
433        }
434
435        public void onServiceDisconnected(int profile) {
436            mService = null;
437            profileReady = false;
438            if (mServiceListeners.size() > 0) {
439                Iterator<ServiceListener> it = mServiceListeners.iterator();
440                while(it.hasNext()) {
441                    it.next().onServiceDisconnected();
442                }
443            }
444        }
445
446        @Override
447        public boolean isProfileReady() {
448            return profileReady;
449        }
450
451        @Override
452        public List<BluetoothDevice> getConnectedDevices() {
453            return mService.getConnectedDevices();
454        }
455
456        @Override
457        public boolean connect(BluetoothDevice device) {
458            return mService.connect(device);
459        }
460
461        @Override
462        public boolean disconnect(BluetoothDevice device) {
463            List<BluetoothDevice> deviceList = getConnectedDevices();
464            if (deviceList.size() != 0 && deviceList.get(0).equals(device)) {
465                // Downgrade priority as user is disconnecting the headset.
466                if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
467                    mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
468                }
469                return mService.disconnect(device);
470            } else {
471                return false;
472            }
473        }
474
475        @Override
476        public int getConnectionStatus(BluetoothDevice device) {
477            List<BluetoothDevice> deviceList = getConnectedDevices();
478
479            return deviceList.size() > 0 && deviceList.get(0).equals(device)
480                    ? convertState(mService.getConnectionState(device))
481                    : SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
482        }
483
484        @Override
485        public int getSummary(BluetoothDevice device) {
486            int connectionStatus = getConnectionStatus(device);
487
488            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
489                return R.string.bluetooth_headset_profile_summary_connected;
490            } else {
491                return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
492            }
493        }
494
495        @Override
496        public boolean isPreferred(BluetoothDevice device) {
497            return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
498        }
499
500        @Override
501        public int getPreferred(BluetoothDevice device) {
502            return mService.getPriority(device);
503        }
504
505        @Override
506        public void setPreferred(BluetoothDevice device, boolean preferred) {
507            if (preferred) {
508                if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
509                    mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
510                }
511            } else {
512                mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
513            }
514        }
515
516        @Override
517        public int convertState(int headsetState) {
518            switch (headsetState) {
519            case BluetoothProfile.STATE_CONNECTED:
520                return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
521            case BluetoothProfile.STATE_CONNECTING:
522                return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
523            case BluetoothProfile.STATE_DISCONNECTED:
524                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
525            default:
526                return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
527            }
528        }
529
530        @Override
531        public int getDrawableResource() {
532            return R.drawable.ic_bt_headset_hfp;
533        }
534    }
535
536    /**
537     * OppProfileManager
538     */
539    private static class OppProfileManager extends LocalBluetoothProfileManager {
540
541        public OppProfileManager(LocalBluetoothManager localManager) {
542            super(localManager);
543        }
544
545        @Override
546        public List<BluetoothDevice> getConnectedDevices() {
547            return null;
548        }
549
550        @Override
551        public boolean connect(BluetoothDevice device) {
552            return false;
553        }
554
555        @Override
556        public boolean disconnect(BluetoothDevice device) {
557            return false;
558        }
559
560        @Override
561        public int getConnectionStatus(BluetoothDevice device) {
562            return -1;
563        }
564
565        @Override
566        public int getSummary(BluetoothDevice device) {
567            int connectionStatus = getConnectionStatus(device);
568
569            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
570                return R.string.bluetooth_opp_profile_summary_connected;
571            } else {
572                return R.string.bluetooth_opp_profile_summary_not_connected;
573            }
574        }
575
576        @Override
577        public boolean isPreferred(BluetoothDevice device) {
578            return false;
579        }
580
581        @Override
582        public int getPreferred(BluetoothDevice device) {
583            return -1;
584        }
585
586        @Override
587        public void setPreferred(BluetoothDevice device, boolean preferred) {
588        }
589
590        @Override
591        public boolean isProfileReady() {
592            return true;
593        }
594
595        @Override
596        public int convertState(int oppState) {
597            switch (oppState) {
598            case 0:
599                return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
600            case 1:
601                return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
602            case 2:
603                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
604            default:
605                return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
606            }
607        }
608
609        @Override
610        public int getDrawableResource() {
611            // TODO:
612            return 0;
613        }
614    }
615
616    private static class HidProfileManager extends LocalBluetoothProfileManager {
617        private BluetoothInputDevice mService;
618
619        public HidProfileManager(LocalBluetoothManager localManager) {
620            super(localManager);
621            mService = new BluetoothInputDevice(localManager.getContext());
622        }
623
624        @Override
625        public boolean connect(BluetoothDevice device) {
626            return mService.connectInputDevice(device);
627        }
628
629        @Override
630        public int convertState(int hidState) {
631            switch (hidState) {
632            case BluetoothInputDevice.STATE_CONNECTED:
633                return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
634            case BluetoothInputDevice.STATE_CONNECTING:
635                return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
636            case BluetoothInputDevice.STATE_DISCONNECTED:
637                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
638            case BluetoothInputDevice.STATE_DISCONNECTING:
639                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING;
640            default:
641                return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
642            }
643        }
644
645        @Override
646        public boolean disconnect(BluetoothDevice device) {
647            return mService.disconnectInputDevice(device);
648        }
649
650        @Override
651        public List<BluetoothDevice> getConnectedDevices() {
652            return mService.getConnectedInputDevices();
653        }
654
655        @Override
656        public int getConnectionStatus(BluetoothDevice device) {
657            return convertState(mService.getInputDeviceState(device));
658        }
659
660        @Override
661        public int getPreferred(BluetoothDevice device) {
662            return mService.getInputDevicePriority(device);
663        }
664
665        @Override
666        public int getSummary(BluetoothDevice device) {
667            final int connectionStatus = getConnectionStatus(device);
668
669            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
670                return R.string.bluetooth_hid_profile_summary_connected;
671            } else {
672                return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
673            }
674        }
675
676        @Override
677        public boolean isPreferred(BluetoothDevice device) {
678            return mService.getInputDevicePriority(device) > BluetoothInputDevice.PRIORITY_OFF;
679        }
680
681        @Override
682        public boolean isProfileReady() {
683            return true;
684        }
685
686        @Override
687        public void setPreferred(BluetoothDevice device, boolean preferred) {
688            if (preferred) {
689                if (mService.getInputDevicePriority(device) < BluetoothInputDevice.PRIORITY_ON) {
690                    mService.setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON);
691                }
692            } else {
693                mService.setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_OFF);
694            }
695        }
696
697        @Override
698        public int getDrawableResource() {
699            // TODO:
700            return 0;
701        }
702    }
703
704    private static class PanProfileManager extends LocalBluetoothProfileManager {
705        private BluetoothPan mService;
706
707        public PanProfileManager(LocalBluetoothManager localManager) {
708            super(localManager);
709            mService = new BluetoothPan(localManager.getContext());
710        }
711
712        @Override
713        public boolean connect(BluetoothDevice device) {
714            return mService.connect(device);
715        }
716
717        @Override
718        public int convertState(int panState) {
719            switch (panState) {
720            case BluetoothPan.STATE_CONNECTED:
721                return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
722            case BluetoothPan.STATE_CONNECTING:
723                return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
724            case BluetoothPan.STATE_DISCONNECTED:
725                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
726            case BluetoothPan.STATE_DISCONNECTING:
727                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING;
728            default:
729                return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
730            }
731        }
732
733        @Override
734        public boolean disconnect(BluetoothDevice device) {
735            return mService.disconnect(device);
736        }
737
738        @Override
739        public int getSummary(BluetoothDevice device) {
740            final int connectionStatus = getConnectionStatus(device);
741
742            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
743                return R.string.bluetooth_pan_profile_summary_connected;
744            } else {
745                return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
746            }
747        }
748
749        @Override
750        public boolean isProfileReady() {
751            return true;
752        }
753
754        @Override
755        public List<BluetoothDevice> getConnectedDevices() {
756            return mService.getConnectedDevices();
757        }
758
759        @Override
760        public int getConnectionStatus(BluetoothDevice device) {
761            return convertState(mService.getPanDeviceState(device));
762        }
763
764        @Override
765        public int getPreferred(BluetoothDevice device) {
766            return -1;
767        }
768
769        @Override
770        public boolean isPreferred(BluetoothDevice device) {
771            return false;
772        }
773
774        @Override
775        public void setPreferred(BluetoothDevice device, boolean preferred) {
776            return;
777        }
778
779        @Override
780        public int getDrawableResource() {
781            // TODO:
782            return 0;
783        }
784    }
785}
786