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