LocalBluetoothProfileManager.java revision c090feb64f674d9840993736a24f9667f8b0e0d5
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    public static enum Profile {
257        HEADSET(R.string.bluetooth_profile_headset),
258        A2DP(R.string.bluetooth_profile_a2dp),
259        OPP(R.string.bluetooth_profile_opp),
260        HID(R.string.bluetooth_profile_hid),
261        PAN(R.string.bluetooth_profile_pan);
262
263        public final int localizedString;
264
265        private Profile(int localizedString) {
266            this.localizedString = localizedString;
267        }
268    }
269
270    /**
271     * A2dpProfileManager is an abstraction for the {@link BluetoothA2dp} service.
272     */
273    private static class A2dpProfileManager extends LocalBluetoothProfileManager
274          implements BluetoothProfile.ServiceListener {
275        private BluetoothA2dp mService;
276
277        // TODO(): The calls must wait for mService. Its not null just
278        // because it runs in the system server.
279        public A2dpProfileManager(LocalBluetoothManager localManager) {
280            super(localManager);
281            BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
282            adapter.getProfileProxy(localManager.getContext(), this, BluetoothProfile.A2DP);
283
284        }
285
286        public void onServiceConnected(int profile, BluetoothProfile proxy) {
287            mService = (BluetoothA2dp) proxy;
288        }
289
290        public void onServiceDisconnected(int profile) {
291            mService = null;
292        }
293
294        @Override
295        public List<BluetoothDevice> getConnectedDevices() {
296            return mService.getDevicesMatchingConnectionStates(
297                  new int[] {BluetoothProfile.STATE_CONNECTED,
298                             BluetoothProfile.STATE_CONNECTING,
299                             BluetoothProfile.STATE_DISCONNECTING});
300        }
301
302        @Override
303        public boolean connect(BluetoothDevice device) {
304            List<BluetoothDevice> sinks = getConnectedDevices();
305            if (sinks != null) {
306                for (BluetoothDevice sink : sinks) {
307                    mService.disconnect(sink);
308                }
309            }
310            return mService.connect(device);
311        }
312
313        @Override
314        public boolean disconnect(BluetoothDevice device) {
315            // Downgrade priority as user is disconnecting the sink.
316            if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
317                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
318            }
319            return mService.disconnect(device);
320        }
321
322        @Override
323        public int getConnectionStatus(BluetoothDevice device) {
324            return convertState(mService.getConnectionState(device));
325        }
326
327        @Override
328        public int getSummary(BluetoothDevice device) {
329            int connectionStatus = getConnectionStatus(device);
330
331            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
332                return R.string.bluetooth_a2dp_profile_summary_connected;
333            } else {
334                return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
335            }
336        }
337
338        @Override
339        public boolean isPreferred(BluetoothDevice device) {
340            return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
341        }
342
343        @Override
344        public int getPreferred(BluetoothDevice device) {
345            return mService.getPriority(device);
346        }
347
348        @Override
349        public void setPreferred(BluetoothDevice device, boolean preferred) {
350            if (preferred) {
351                if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
352                    mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
353                }
354            } else {
355                mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
356            }
357        }
358
359        @Override
360        public int convertState(int a2dpState) {
361            switch (a2dpState) {
362            case BluetoothProfile.STATE_CONNECTED:
363                return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
364            case BluetoothProfile.STATE_CONNECTING:
365                return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
366            case BluetoothProfile.STATE_DISCONNECTED:
367                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
368            case BluetoothProfile.STATE_DISCONNECTING:
369                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING;
370            case BluetoothA2dp.STATE_PLAYING:
371                return SettingsBtStatus.CONNECTION_STATUS_ACTIVE;
372            default:
373                return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
374            }
375        }
376
377        @Override
378        public boolean isProfileReady() {
379            return true;
380        }
381
382        @Override
383        public int getDrawableResource() {
384            return R.drawable.ic_bt_headphones_a2dp;
385        }
386    }
387
388    /**
389     * HeadsetProfileManager is an abstraction for the {@link BluetoothHeadset} service.
390     */
391    private static class HeadsetProfileManager extends LocalBluetoothProfileManager
392            implements BluetoothProfile.ServiceListener {
393        private BluetoothHeadset mService;
394        private Handler mUiHandler = new Handler();
395        private boolean profileReady = false;
396
397        // TODO(): The calls must get queued if mService becomes null.
398        // It can happen  when phone app crashes for some reason.
399        // All callers should have service listeners. Dock Service is the only
400        // one right now.
401        public HeadsetProfileManager(LocalBluetoothManager localManager) {
402            super(localManager);
403            BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
404            adapter.getProfileProxy(localManager.getContext(), this, BluetoothProfile.HEADSET);
405        }
406
407        public void onServiceConnected(int profile, BluetoothProfile proxy) {
408            mService = (BluetoothHeadset) proxy;
409            profileReady = true;
410            // This could be called on a non-UI thread, funnel to UI thread.
411            mUiHandler.post(new Runnable() {
412                public void run() {
413                    /*
414                     * We just bound to the service, so refresh the UI of the
415                     * headset device.
416                     */
417                    List<BluetoothDevice> deviceList = mService.getConnectedDevices();
418                    if (deviceList.size() == 0) return;
419
420                    mLocalManager.getCachedDeviceManager()
421                            .onProfileStateChanged(deviceList.get(0), Profile.HEADSET,
422                                                   BluetoothProfile.STATE_CONNECTED);
423                }
424            });
425
426            if (mServiceListeners.size() > 0) {
427                Iterator<ServiceListener> it = mServiceListeners.iterator();
428                while(it.hasNext()) {
429                    it.next().onServiceConnected();
430                }
431            }
432        }
433
434        public void onServiceDisconnected(int profile) {
435            mService = null;
436            profileReady = false;
437            if (mServiceListeners.size() > 0) {
438                Iterator<ServiceListener> it = mServiceListeners.iterator();
439                while(it.hasNext()) {
440                    it.next().onServiceDisconnected();
441                }
442            }
443        }
444
445        @Override
446        public boolean isProfileReady() {
447            return profileReady;
448        }
449
450        @Override
451        public List<BluetoothDevice> getConnectedDevices() {
452            return mService.getConnectedDevices();
453        }
454
455        @Override
456        public boolean connect(BluetoothDevice device) {
457            return mService.connect(device);
458        }
459
460        @Override
461        public boolean disconnect(BluetoothDevice device) {
462            List<BluetoothDevice> deviceList = getConnectedDevices();
463            if (deviceList.size() != 0 && deviceList.get(0).equals(device)) {
464                // Downgrade priority as user is disconnecting the headset.
465                if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
466                    mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
467                }
468                return mService.disconnect(device);
469            } else {
470                return false;
471            }
472        }
473
474        @Override
475        public int getConnectionStatus(BluetoothDevice device) {
476            List<BluetoothDevice> deviceList = getConnectedDevices();
477
478            return deviceList.size() > 0 && deviceList.get(0).equals(device)
479                    ? convertState(mService.getConnectionState(device))
480                    : SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
481        }
482
483        @Override
484        public int getSummary(BluetoothDevice device) {
485            int connectionStatus = getConnectionStatus(device);
486
487            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
488                return R.string.bluetooth_headset_profile_summary_connected;
489            } else {
490                return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
491            }
492        }
493
494        @Override
495        public boolean isPreferred(BluetoothDevice device) {
496            return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
497        }
498
499        @Override
500        public int getPreferred(BluetoothDevice device) {
501            return mService.getPriority(device);
502        }
503
504        @Override
505        public void setPreferred(BluetoothDevice device, boolean preferred) {
506            if (preferred) {
507                if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
508                    mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
509                }
510            } else {
511                mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
512            }
513        }
514
515        @Override
516        public int convertState(int headsetState) {
517            switch (headsetState) {
518            case BluetoothProfile.STATE_CONNECTED:
519                return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
520            case BluetoothProfile.STATE_CONNECTING:
521                return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
522            case BluetoothProfile.STATE_DISCONNECTED:
523                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
524            default:
525                return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
526            }
527        }
528
529        @Override
530        public int getDrawableResource() {
531            return R.drawable.ic_bt_headset_hfp;
532        }
533    }
534
535    /**
536     * OppProfileManager
537     */
538    private static class OppProfileManager extends LocalBluetoothProfileManager {
539
540        public OppProfileManager(LocalBluetoothManager localManager) {
541            super(localManager);
542        }
543
544        @Override
545        public List<BluetoothDevice> getConnectedDevices() {
546            return null;
547        }
548
549        @Override
550        public boolean connect(BluetoothDevice device) {
551            return false;
552        }
553
554        @Override
555        public boolean disconnect(BluetoothDevice device) {
556            return false;
557        }
558
559        @Override
560        public int getConnectionStatus(BluetoothDevice device) {
561            return -1;
562        }
563
564        @Override
565        public int getSummary(BluetoothDevice device) {
566            int connectionStatus = getConnectionStatus(device);
567
568            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
569                return R.string.bluetooth_opp_profile_summary_connected;
570            } else {
571                return R.string.bluetooth_opp_profile_summary_not_connected;
572            }
573        }
574
575        @Override
576        public boolean isPreferred(BluetoothDevice device) {
577            return false;
578        }
579
580        @Override
581        public int getPreferred(BluetoothDevice device) {
582            return -1;
583        }
584
585        @Override
586        public void setPreferred(BluetoothDevice device, boolean preferred) {
587        }
588
589        @Override
590        public boolean isProfileReady() {
591            return true;
592        }
593
594        @Override
595        public int convertState(int oppState) {
596            switch (oppState) {
597            case 0:
598                return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
599            case 1:
600                return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
601            case 2:
602                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
603            default:
604                return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
605            }
606        }
607
608        @Override
609        public int getDrawableResource() {
610            // TODO:
611            return 0;
612        }
613    }
614
615    private static class HidProfileManager extends LocalBluetoothProfileManager {
616        private BluetoothInputDevice mService;
617
618        public HidProfileManager(LocalBluetoothManager localManager) {
619            super(localManager);
620            mService = new BluetoothInputDevice(localManager.getContext());
621        }
622
623        @Override
624        public boolean connect(BluetoothDevice device) {
625            return mService.connectInputDevice(device);
626        }
627
628        @Override
629        public int convertState(int hidState) {
630            switch (hidState) {
631            case BluetoothInputDevice.STATE_CONNECTED:
632                return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
633            case BluetoothInputDevice.STATE_CONNECTING:
634                return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
635            case BluetoothInputDevice.STATE_DISCONNECTED:
636                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
637            case BluetoothInputDevice.STATE_DISCONNECTING:
638                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING;
639            default:
640                return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
641            }
642        }
643
644        @Override
645        public boolean disconnect(BluetoothDevice device) {
646            return mService.disconnectInputDevice(device);
647        }
648
649        @Override
650        public List<BluetoothDevice> getConnectedDevices() {
651            return mService.getConnectedInputDevices();
652        }
653
654        @Override
655        public int getConnectionStatus(BluetoothDevice device) {
656            return convertState(mService.getInputDeviceState(device));
657        }
658
659        @Override
660        public int getPreferred(BluetoothDevice device) {
661            return mService.getInputDevicePriority(device);
662        }
663
664        @Override
665        public int getSummary(BluetoothDevice device) {
666            final int connectionStatus = getConnectionStatus(device);
667
668            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
669                return R.string.bluetooth_hid_profile_summary_connected;
670            } else {
671                return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
672            }
673        }
674
675        @Override
676        public boolean isPreferred(BluetoothDevice device) {
677            return mService.getInputDevicePriority(device) > BluetoothInputDevice.PRIORITY_OFF;
678        }
679
680        @Override
681        public boolean isProfileReady() {
682            return true;
683        }
684
685        @Override
686        public void setPreferred(BluetoothDevice device, boolean preferred) {
687            if (preferred) {
688                if (mService.getInputDevicePriority(device) < BluetoothInputDevice.PRIORITY_ON) {
689                    mService.setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON);
690                }
691            } else {
692                mService.setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_OFF);
693            }
694        }
695
696        @Override
697        public int getDrawableResource() {
698            // TODO:
699            return 0;
700        }
701    }
702
703    private static class PanProfileManager extends LocalBluetoothProfileManager {
704        private BluetoothPan mService;
705
706        public PanProfileManager(LocalBluetoothManager localManager) {
707            super(localManager);
708            mService = new BluetoothPan(localManager.getContext());
709        }
710
711        @Override
712        public boolean connect(BluetoothDevice device) {
713            return mService.connect(device);
714        }
715
716        @Override
717        public int convertState(int panState) {
718            switch (panState) {
719            case BluetoothPan.STATE_CONNECTED:
720                return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
721            case BluetoothPan.STATE_CONNECTING:
722                return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
723            case BluetoothPan.STATE_DISCONNECTED:
724                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
725            case BluetoothPan.STATE_DISCONNECTING:
726                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING;
727            default:
728                return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
729            }
730        }
731
732        @Override
733        public boolean disconnect(BluetoothDevice device) {
734            return mService.disconnect(device);
735        }
736
737        @Override
738        public int getSummary(BluetoothDevice device) {
739            final int connectionStatus = getConnectionStatus(device);
740
741            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
742                return R.string.bluetooth_pan_profile_summary_connected;
743            } else {
744                return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
745            }
746        }
747
748        @Override
749        public boolean isProfileReady() {
750            return true;
751        }
752
753        @Override
754        public List<BluetoothDevice> getConnectedDevices() {
755            return mService.getConnectedDevices();
756        }
757
758        @Override
759        public int getConnectionStatus(BluetoothDevice device) {
760            return convertState(mService.getPanDeviceState(device));
761        }
762
763        @Override
764        public int getPreferred(BluetoothDevice device) {
765            return -1;
766        }
767
768        @Override
769        public boolean isPreferred(BluetoothDevice device) {
770            return false;
771        }
772
773        @Override
774        public void setPreferred(BluetoothDevice device, boolean preferred) {
775            return;
776        }
777
778        @Override
779        public int getDrawableResource() {
780            // TODO:
781            return 0;
782        }
783    }
784}
785