• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.settings.bluetooth;
18 
19 import android.bluetooth.BluetoothA2dp;
20 import android.bluetooth.BluetoothDevice;
21 import android.bluetooth.BluetoothHeadset;
22 import android.bluetooth.BluetoothUuid;
23 import android.os.ParcelUuid;
24 import android.os.Handler;
25 import android.util.Log;
26 
27 import com.android.settings.R;
28 
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Set;
33 
34 /**
35  * LocalBluetoothProfileManager is an abstract class defining the basic
36  * functionality related to a profile.
37  */
38 public abstract class LocalBluetoothProfileManager {
39     private static final String TAG = "LocalBluetoothProfileManager";
40 
41     /* package */ static final ParcelUuid[] HEADSET_PROFILE_UUIDS = new ParcelUuid[] {
42         BluetoothUuid.HSP,
43         BluetoothUuid.Handsfree,
44     };
45 
46     /* package */ static final ParcelUuid[] A2DP_PROFILE_UUIDS = new ParcelUuid[] {
47         BluetoothUuid.AudioSink,
48         BluetoothUuid.AdvAudioDist,
49     };
50 
51     /* package */ static final ParcelUuid[] OPP_PROFILE_UUIDS = new ParcelUuid[] {
52         BluetoothUuid.ObexObjectPush
53     };
54 
55     // TODO: close profiles when we're shutting down
56     private static Map<Profile, LocalBluetoothProfileManager> sProfileMap =
57             new HashMap<Profile, LocalBluetoothProfileManager>();
58 
59     protected LocalBluetoothManager mLocalManager;
60 
init(LocalBluetoothManager localManager)61     public static void init(LocalBluetoothManager localManager) {
62         synchronized (sProfileMap) {
63             if (sProfileMap.size() == 0) {
64                 LocalBluetoothProfileManager profileManager;
65 
66                 profileManager = new A2dpProfileManager(localManager);
67                 sProfileMap.put(Profile.A2DP, profileManager);
68 
69                 profileManager = new HeadsetProfileManager(localManager);
70                 sProfileMap.put(Profile.HEADSET, profileManager);
71 
72                 profileManager = new OppProfileManager(localManager);
73                 sProfileMap.put(Profile.OPP, profileManager);
74             }
75         }
76     }
77 
getProfileManager(LocalBluetoothManager localManager, Profile profile)78     public static LocalBluetoothProfileManager getProfileManager(LocalBluetoothManager localManager,
79             Profile profile) {
80         // Note: This code assumes that "localManager" is same as the
81         // LocalBluetoothManager that was used to initialize the sProfileMap.
82         // If that every changes, we can't just keep one copy of sProfileMap.
83         synchronized (sProfileMap) {
84             LocalBluetoothProfileManager profileManager = sProfileMap.get(profile);
85             if (profileManager == null) {
86                 Log.e(TAG, "profileManager can't be found for " + profile.toString());
87             }
88             return profileManager;
89         }
90     }
91 
92     /**
93      * Temporary method to fill profiles based on a device's class.
94      *
95      * NOTE: This list happens to define the connection order. We should put this logic in a more
96      * well known place when this method is no longer temporary.
97      * @param uuids of the remote device
98      * @param profiles The list of profiles to fill
99      */
updateProfiles(ParcelUuid[] uuids, List<Profile> profiles)100     public static void updateProfiles(ParcelUuid[] uuids, List<Profile> profiles) {
101         profiles.clear();
102 
103         if (uuids == null) {
104             return;
105         }
106 
107         if (BluetoothUuid.containsAnyUuid(uuids, HEADSET_PROFILE_UUIDS)) {
108             profiles.add(Profile.HEADSET);
109         }
110 
111         if (BluetoothUuid.containsAnyUuid(uuids, A2DP_PROFILE_UUIDS)) {
112             profiles.add(Profile.A2DP);
113         }
114 
115         if (BluetoothUuid.containsAnyUuid(uuids, OPP_PROFILE_UUIDS)) {
116             profiles.add(Profile.OPP);
117         }
118     }
119 
LocalBluetoothProfileManager(LocalBluetoothManager localManager)120     protected LocalBluetoothProfileManager(LocalBluetoothManager localManager) {
121         mLocalManager = localManager;
122     }
123 
connect(BluetoothDevice device)124     public abstract boolean connect(BluetoothDevice device);
125 
disconnect(BluetoothDevice device)126     public abstract boolean disconnect(BluetoothDevice device);
127 
getConnectionStatus(BluetoothDevice device)128     public abstract int getConnectionStatus(BluetoothDevice device);
129 
getSummary(BluetoothDevice device)130     public abstract int getSummary(BluetoothDevice device);
131 
convertState(int a2dpState)132     public abstract int convertState(int a2dpState);
133 
isPreferred(BluetoothDevice device)134     public abstract boolean isPreferred(BluetoothDevice device);
135 
setPreferred(BluetoothDevice device, boolean preferred)136     public abstract void setPreferred(BluetoothDevice device, boolean preferred);
137 
isConnected(BluetoothDevice device)138     public boolean isConnected(BluetoothDevice device) {
139         return SettingsBtStatus.isConnectionStatusConnected(getConnectionStatus(device));
140     }
141 
142     // TODO: int instead of enum
143     public enum Profile {
144         HEADSET(R.string.bluetooth_profile_headset),
145         A2DP(R.string.bluetooth_profile_a2dp),
146         OPP(R.string.bluetooth_profile_opp);
147 
148         public final int localizedString;
149 
Profile(int localizedString)150         private Profile(int localizedString) {
151             this.localizedString = localizedString;
152         }
153     }
154 
155     /**
156      * A2dpProfileManager is an abstraction for the {@link BluetoothA2dp} service.
157      */
158     private static class A2dpProfileManager extends LocalBluetoothProfileManager {
159         private BluetoothA2dp mService;
160 
A2dpProfileManager(LocalBluetoothManager localManager)161         public A2dpProfileManager(LocalBluetoothManager localManager) {
162             super(localManager);
163             mService = new BluetoothA2dp(localManager.getContext());
164         }
165 
166         @Override
connect(BluetoothDevice device)167         public boolean connect(BluetoothDevice device) {
168             Set<BluetoothDevice> sinks = mService.getConnectedSinks();
169             if (sinks != null) {
170                 for (BluetoothDevice sink : sinks) {
171                     mService.disconnectSink(sink);
172                 }
173             }
174             return mService.connectSink(device);
175         }
176 
177         @Override
disconnect(BluetoothDevice device)178         public boolean disconnect(BluetoothDevice device) {
179             return mService.disconnectSink(device);
180         }
181 
182         @Override
getConnectionStatus(BluetoothDevice device)183         public int getConnectionStatus(BluetoothDevice device) {
184             return convertState(mService.getSinkState(device));
185         }
186 
187         @Override
getSummary(BluetoothDevice device)188         public int getSummary(BluetoothDevice device) {
189             int connectionStatus = getConnectionStatus(device);
190 
191             if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
192                 return R.string.bluetooth_a2dp_profile_summary_connected;
193             } else {
194                 return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
195             }
196         }
197 
198         @Override
isPreferred(BluetoothDevice device)199         public boolean isPreferred(BluetoothDevice device) {
200             return mService.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
201         }
202 
203         @Override
setPreferred(BluetoothDevice device, boolean preferred)204         public void setPreferred(BluetoothDevice device, boolean preferred) {
205             mService.setSinkPriority(device,
206                     preferred ? BluetoothA2dp.PRIORITY_AUTO : BluetoothA2dp.PRIORITY_OFF);
207         }
208 
209         @Override
convertState(int a2dpState)210         public int convertState(int a2dpState) {
211             switch (a2dpState) {
212             case BluetoothA2dp.STATE_CONNECTED:
213                 return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
214             case BluetoothA2dp.STATE_CONNECTING:
215                 return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
216             case BluetoothA2dp.STATE_DISCONNECTED:
217                 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
218             case BluetoothA2dp.STATE_DISCONNECTING:
219                 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING;
220             case BluetoothA2dp.STATE_PLAYING:
221                 return SettingsBtStatus.CONNECTION_STATUS_ACTIVE;
222             default:
223                 return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
224             }
225         }
226     }
227 
228     /**
229      * HeadsetProfileManager is an abstraction for the {@link BluetoothHeadset} service.
230      */
231     private static class HeadsetProfileManager extends LocalBluetoothProfileManager
232             implements BluetoothHeadset.ServiceListener {
233         private BluetoothHeadset mService;
234         private Handler mUiHandler = new Handler();
235 
HeadsetProfileManager(LocalBluetoothManager localManager)236         public HeadsetProfileManager(LocalBluetoothManager localManager) {
237             super(localManager);
238             mService = new BluetoothHeadset(localManager.getContext(), this);
239         }
240 
onServiceConnected()241         public void onServiceConnected() {
242             // This could be called on a non-UI thread, funnel to UI thread.
243             mUiHandler.post(new Runnable() {
244                 public void run() {
245                     /*
246                      * We just bound to the service, so refresh the UI of the
247                      * headset device.
248                      */
249                     BluetoothDevice device = mService.getCurrentHeadset();
250                     if (device == null) return;
251                     mLocalManager.getCachedDeviceManager()
252                             .onProfileStateChanged(device, Profile.HEADSET,
253                                                    BluetoothHeadset.STATE_CONNECTED);
254                 }
255             });
256         }
257 
onServiceDisconnected()258         public void onServiceDisconnected() {
259         }
260 
261         @Override
connect(BluetoothDevice device)262         public boolean connect(BluetoothDevice device) {
263             // Since connectHeadset fails if already connected to a headset, we
264             // disconnect from any headset first
265             mService.disconnectHeadset();
266             return mService.connectHeadset(device);
267         }
268 
269         @Override
disconnect(BluetoothDevice device)270         public boolean disconnect(BluetoothDevice device) {
271             if (mService.getCurrentHeadset().equals(device)) {
272                 return mService.disconnectHeadset();
273             } else {
274                 return false;
275             }
276         }
277 
278         @Override
getConnectionStatus(BluetoothDevice device)279         public int getConnectionStatus(BluetoothDevice device) {
280             BluetoothDevice currentDevice = mService.getCurrentHeadset();
281             return currentDevice != null && currentDevice.equals(device)
282                     ? convertState(mService.getState())
283                     : SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
284         }
285 
286         @Override
getSummary(BluetoothDevice device)287         public int getSummary(BluetoothDevice device) {
288             int connectionStatus = getConnectionStatus(device);
289 
290             if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
291                 return R.string.bluetooth_headset_profile_summary_connected;
292             } else {
293                 return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
294             }
295         }
296 
297         @Override
isPreferred(BluetoothDevice device)298         public boolean isPreferred(BluetoothDevice device) {
299             return mService.getPriority(device) > BluetoothHeadset.PRIORITY_OFF;
300         }
301 
302         @Override
setPreferred(BluetoothDevice device, boolean preferred)303         public void setPreferred(BluetoothDevice device, boolean preferred) {
304             mService.setPriority(device,
305                     preferred ? BluetoothHeadset.PRIORITY_AUTO : BluetoothHeadset.PRIORITY_OFF);
306         }
307 
308         @Override
convertState(int headsetState)309         public int convertState(int headsetState) {
310             switch (headsetState) {
311             case BluetoothHeadset.STATE_CONNECTED:
312                 return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
313             case BluetoothHeadset.STATE_CONNECTING:
314                 return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
315             case BluetoothHeadset.STATE_DISCONNECTED:
316                 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
317             default:
318                 return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
319             }
320         }
321     }
322 
323     /**
324      * OppProfileManager
325      */
326     private static class OppProfileManager extends LocalBluetoothProfileManager {
327 
OppProfileManager(LocalBluetoothManager localManager)328         public OppProfileManager(LocalBluetoothManager localManager) {
329             super(localManager);
330         }
331 
332         @Override
connect(BluetoothDevice device)333         public boolean connect(BluetoothDevice device) {
334             return false;
335         }
336 
337         @Override
disconnect(BluetoothDevice device)338         public boolean disconnect(BluetoothDevice device) {
339             return false;
340         }
341 
342         @Override
getConnectionStatus(BluetoothDevice device)343         public int getConnectionStatus(BluetoothDevice device) {
344             return -1;
345         }
346 
347         @Override
getSummary(BluetoothDevice device)348         public int getSummary(BluetoothDevice device) {
349             int connectionStatus = getConnectionStatus(device);
350 
351             if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
352                 return R.string.bluetooth_opp_profile_summary_connected;
353             } else {
354                 return R.string.bluetooth_opp_profile_summary_not_connected;
355             }
356         }
357 
358         @Override
isPreferred(BluetoothDevice device)359         public boolean isPreferred(BluetoothDevice device) {
360             return false;
361         }
362 
363         @Override
setPreferred(BluetoothDevice device, boolean preferred)364         public void setPreferred(BluetoothDevice device, boolean preferred) {
365         }
366 
367         @Override
convertState(int oppState)368         public int convertState(int oppState) {
369             switch (oppState) {
370             case 0:
371                 return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
372             case 1:
373                 return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
374             case 2:
375                 return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
376             default:
377                 return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
378             }
379         }
380     }
381 }
382