OSDN Git Service

Merge "Updated BT imaging icons"
[android-x86/packages-apps-Settings.git] / src / com / android / settings / bluetooth / LocalBluetoothProfileManager.java
1 /*
2  * Copyright (C) 2011 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.BluetoothInputDevice;
23 import android.bluetooth.BluetoothPan;
24 import android.bluetooth.BluetoothProfile;
25 import android.bluetooth.BluetoothUuid;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.os.ParcelUuid;
29 import android.util.Log;
30
31 import java.util.ArrayList;
32 import java.util.Collection;
33 import java.util.HashMap;
34 import java.util.Map;
35
36 /**
37  * LocalBluetoothProfileManager provides access to the LocalBluetoothProfile
38  * objects for the available Bluetooth profiles.
39  */
40 final class LocalBluetoothProfileManager {
41     private static final String TAG = "LocalBluetoothProfileManager";
42
43     /** Singleton instance. */
44     private static LocalBluetoothProfileManager sInstance;
45
46     /**
47      * An interface for notifying BluetoothHeadset IPC clients when they have
48      * been connected to the BluetoothHeadset service.
49      * Only used by {@link DockService}.
50      */
51     public interface ServiceListener {
52         /**
53          * Called to notify the client when this proxy object has been
54          * connected to the BluetoothHeadset service. Clients must wait for
55          * this callback before making IPC calls on the BluetoothHeadset
56          * service.
57          */
58         void onServiceConnected();
59
60         /**
61          * Called to notify the client that this proxy object has been
62          * disconnected from the BluetoothHeadset service. Clients must not
63          * make IPC calls on the BluetoothHeadset service after this callback.
64          * This callback will currently only occur if the application hosting
65          * the BluetoothHeadset service, but may be called more often in future.
66          */
67         void onServiceDisconnected();
68     }
69
70     private final Context mContext;
71     private final LocalBluetoothAdapter mLocalAdapter;
72     private final CachedBluetoothDeviceManager mDeviceManager;
73     private final BluetoothEventManager mEventManager;
74
75     private A2dpProfile mA2dpProfile;
76     private HeadsetProfile mHeadsetProfile;
77     private final HidProfile mHidProfile;
78     private OppProfile mOppProfile;
79     private final PanProfile mPanProfile;
80
81     /**
82      * Mapping from profile name, e.g. "HEADSET" to profile object.
83      */
84     private final Map<String, LocalBluetoothProfile>
85             mProfileNameMap = new HashMap<String, LocalBluetoothProfile>();
86
87     LocalBluetoothProfileManager(Context context,
88             LocalBluetoothAdapter adapter,
89             CachedBluetoothDeviceManager deviceManager,
90             BluetoothEventManager eventManager) {
91         mContext = context;
92
93         mLocalAdapter = adapter;
94         mDeviceManager = deviceManager;
95         mEventManager = eventManager;
96         // pass this reference to adapter and event manager (circular dependency)
97         mLocalAdapter.setProfileManager(this);
98         mEventManager.setProfileManager(this);
99
100         ParcelUuid[] uuids = adapter.getUuids();
101
102         // uuids may be null if Bluetooth is turned off
103         if (uuids != null) {
104             updateLocalProfiles(uuids);
105         }
106
107         // Always add HID and PAN profiles
108         mHidProfile = new HidProfile(context, mLocalAdapter);
109         addProfile(mHidProfile, HidProfile.NAME,
110                 BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
111
112         mPanProfile = new PanProfile(context);
113         addProfile(mPanProfile, PanProfile.NAME, BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
114         Log.d(TAG, "LocalBluetoothProfileManager construction complete");
115     }
116
117     /**
118      * Initialize or update the local profile objects. If a UUID was previously
119      * present but has been removed, we print a warning but don't remove the
120      * profile object as it might be referenced elsewhere, or the UUID might
121      * come back and we don't want multiple copies of the profile objects.
122      * @param uuids
123      */
124     void updateLocalProfiles(ParcelUuid[] uuids) {
125         // A2DP
126         if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSource)) {
127             if (mA2dpProfile == null) {
128                 Log.d(TAG, "Adding local A2DP profile");
129                 mA2dpProfile = new A2dpProfile(mContext);
130                 addProfile(mA2dpProfile, A2dpProfile.NAME,
131                         BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
132             }
133         } else if (mA2dpProfile != null) {
134             Log.w(TAG, "Warning: A2DP profile was previously added but the UUID is now missing.");
135         }
136
137         // Headset / Handsfree
138         if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) ||
139             BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP_AG)) {
140             if (mHeadsetProfile == null) {
141                 Log.d(TAG, "Adding local HEADSET profile");
142                 mHeadsetProfile = new HeadsetProfile(mContext, mLocalAdapter,
143                         mDeviceManager, this);
144                 addProfile(mHeadsetProfile, HeadsetProfile.NAME,
145                         BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
146             }
147         } else if (mHeadsetProfile != null) {
148             Log.w(TAG, "Warning: HEADSET profile was previously added but the UUID is now missing.");
149         }
150
151         // OPP
152         if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) {
153             if (mOppProfile == null) {
154                 Log.d(TAG, "Adding local OPP profile");
155                 mOppProfile = new OppProfile();
156                 // Note: no event handler for OPP, only name map.
157                 mProfileNameMap.put(OppProfile.NAME, mOppProfile);
158             }
159         } else if (mOppProfile != null) {
160             Log.w(TAG, "Warning: OPP profile was previously added but the UUID is now missing.");
161         }
162
163         // There is no local SDP record for HID and Settings app doesn't control PBAP
164     }
165
166     private final Collection<ServiceListener> mServiceListeners =
167             new ArrayList<ServiceListener>();
168
169     private void addProfile(LocalBluetoothProfile profile,
170             String profileName, String stateChangedAction) {
171         mEventManager.addHandler(stateChangedAction, new StateChangedHandler(profile));
172         mProfileNameMap.put(profileName, profile);
173     }
174
175     LocalBluetoothProfile getProfileByName(String name) {
176         return mProfileNameMap.get(name);
177     }
178
179     // Called from LocalBluetoothAdapter when state changes to ON
180     void setBluetoothStateOn() {
181         ParcelUuid[] uuids = mLocalAdapter.getUuids();
182         if (uuids != null) {
183             updateLocalProfiles(uuids);
184         }
185         mEventManager.readPairedDevices();
186     }
187
188     /**
189      * Generic handler for connection state change events for the specified profile.
190      */
191     private class StateChangedHandler implements BluetoothEventManager.Handler {
192         private final LocalBluetoothProfile mProfile;
193
194         StateChangedHandler(LocalBluetoothProfile profile) {
195             mProfile = profile;
196         }
197
198         public void onReceive(Context context, Intent intent, BluetoothDevice device) {
199             CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
200             if (cachedDevice == null) {
201                 Log.w(TAG, "StateChangedHandler found new device: " + device);
202                 cachedDevice = mDeviceManager.addDevice(mLocalAdapter,
203                         LocalBluetoothProfileManager.this, device);
204             }
205             int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
206             int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
207             if (newState == BluetoothProfile.STATE_DISCONNECTED &&
208                     oldState == BluetoothProfile.STATE_CONNECTING) {
209                 Log.i(TAG, "Failed to connect " + mProfile + " device");
210             }
211
212             cachedDevice.onProfileStateChanged(mProfile, newState);
213             cachedDevice.refresh();
214         }
215     }
216
217     // called from DockService
218     void addServiceListener(ServiceListener l) {
219         mServiceListeners.add(l);
220     }
221
222     // called from DockService
223     void removeServiceListener(ServiceListener l) {
224         mServiceListeners.remove(l);
225     }
226
227     // not synchronized: use only from UI thread! (TODO: verify)
228     void callServiceConnectedListeners() {
229         for (ServiceListener l : mServiceListeners) {
230             l.onServiceConnected();
231         }
232     }
233
234     // not synchronized: use only from UI thread! (TODO: verify)
235     void callServiceDisconnectedListeners() {
236         for (ServiceListener listener : mServiceListeners) {
237             listener.onServiceDisconnected();
238         }
239     }
240
241     // This is called by DockService, so check Headset and A2DP.
242     public synchronized boolean isManagerReady() {
243         // Getting just the headset profile is fine for now. Will need to deal with A2DP
244         // and others if they aren't always in a ready state.
245         LocalBluetoothProfile profile = mHeadsetProfile;
246         if (profile != null) {
247             return profile.isProfileReady();
248         }
249         profile = mA2dpProfile;
250         if (profile != null) {
251             return profile.isProfileReady();
252         }
253         return false;
254     }
255
256     A2dpProfile getA2dpProfile() {
257         return mA2dpProfile;
258     }
259
260     HeadsetProfile getHeadsetProfile() {
261         return mHeadsetProfile;
262     }
263
264     /**
265      * Fill in a list of LocalBluetoothProfile objects that are supported by
266      * the local device and the remote device.
267      *
268      * @param uuids of the remote device
269      * @param localUuids UUIDs of the local device
270      * @param profiles The list of profiles to fill
271      */
272     synchronized void updateProfiles(ParcelUuid[] uuids, ParcelUuid[] localUuids,
273         Collection<LocalBluetoothProfile> profiles) {
274         profiles.clear();
275
276         if (uuids == null) {
277             return;
278         }
279
280         if (mHeadsetProfile != null) {
281             if ((BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.HSP_AG) &&
282                 BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP)) ||
283                 (BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree_AG) &&
284                 BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree))) {
285                     profiles.add(mHeadsetProfile);
286             }
287         }
288
289         if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS) &&
290             mA2dpProfile != null) {
291             profiles.add(mA2dpProfile);
292         }
293
294         if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush) &&
295             mOppProfile != null) {
296             profiles.add(mOppProfile);
297         }
298
299         if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hid) &&
300             mHidProfile != null) {
301             profiles.add(mHidProfile);
302         }
303
304         if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP) &&
305             mPanProfile != null) {
306             profiles.add(mPanProfile);
307         }
308     }
309 }