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