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