LocalBluetoothProfileManager.java revision d63c0112251ab4e4e977545368dd703d875012a4
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 android.bluetooth.BluetoothA2dp;
20import android.bluetooth.BluetoothDevice;
21import android.bluetooth.BluetoothError;
22import android.bluetooth.BluetoothHeadset;
23import android.os.Handler;
24import android.text.TextUtils;
25
26import com.android.settings.R;
27
28import java.util.HashMap;
29import java.util.List;
30import java.util.Map;
31import java.util.Set;
32
33/**
34 * LocalBluetoothProfileManager is an abstract class defining the basic
35 * functionality related to a profile.
36 */
37public abstract class LocalBluetoothProfileManager {
38
39    // TODO: close profiles when we're shutting down
40    private static Map<Profile, LocalBluetoothProfileManager> sProfileMap =
41            new HashMap<Profile, LocalBluetoothProfileManager>();
42
43    protected LocalBluetoothManager mLocalManager;
44
45    public static LocalBluetoothProfileManager getProfileManager(LocalBluetoothManager localManager,
46            Profile profile) {
47
48        LocalBluetoothProfileManager profileManager;
49
50        synchronized (sProfileMap) {
51            profileManager = sProfileMap.get(profile);
52
53            if (profileManager == null) {
54                switch (profile) {
55                case A2DP:
56                    profileManager = new A2dpProfileManager(localManager);
57                    break;
58
59                case HEADSET:
60                    profileManager = new HeadsetProfileManager(localManager);
61                    break;
62                }
63
64                sProfileMap.put(profile, profileManager);
65            }
66        }
67
68        return profileManager;
69    }
70
71    /**
72     * Temporary method to fill profiles based on a device's class.
73     *
74     * NOTE: This list happens to define the connection order. We should put this logic in a more
75     * well known place when this method is no longer temporary.
76     *
77     * @param btClass The class
78     * @param profiles The list of profiles to fill
79     */
80    public static void fill(int btClass, List<Profile> profiles) {
81        profiles.clear();
82
83        if (BluetoothHeadset.doesClassMatch(btClass)) {
84            profiles.add(Profile.HEADSET);
85        }
86
87        if (BluetoothA2dp.doesClassMatchSink(btClass)) {
88            profiles.add(Profile.A2DP);
89        }
90    }
91
92    protected LocalBluetoothProfileManager(LocalBluetoothManager localManager) {
93        mLocalManager = localManager;
94    }
95
96    public abstract int connect(BluetoothDevice device);
97
98    public abstract int disconnect(BluetoothDevice device);
99
100    public abstract int getConnectionStatus(BluetoothDevice device);
101
102    public abstract int getSummary(BluetoothDevice device);
103
104    public abstract int convertState(int a2dpState);
105
106    public abstract boolean isPreferred(BluetoothDevice device);
107
108    public abstract void setPreferred(BluetoothDevice device, boolean preferred);
109
110    public boolean isConnected(BluetoothDevice device) {
111        return SettingsBtStatus.isConnectionStatusConnected(getConnectionStatus(device));
112    }
113
114    // TODO: int instead of enum
115    public enum Profile {
116        HEADSET(R.string.bluetooth_profile_headset),
117        A2DP(R.string.bluetooth_profile_a2dp);
118
119        public final int localizedString;
120
121        private Profile(int localizedString) {
122            this.localizedString = localizedString;
123        }
124    }
125
126    /**
127     * A2dpProfileManager is an abstraction for the {@link BluetoothA2dp} service.
128     */
129    private static class A2dpProfileManager extends LocalBluetoothProfileManager {
130        private BluetoothA2dp mService;
131
132        public A2dpProfileManager(LocalBluetoothManager localManager) {
133            super(localManager);
134            mService = new BluetoothA2dp(localManager.getContext());
135        }
136
137        @Override
138        public int connect(BluetoothDevice device) {
139            Set<BluetoothDevice> sinks = mService.getConnectedSinks();
140            if (sinks != null) {
141                for (BluetoothDevice sink : sinks) {
142                    mService.disconnectSink(sink);
143                }
144            }
145            return mService.connectSink(device);
146        }
147
148        @Override
149        public int disconnect(BluetoothDevice device) {
150            return mService.disconnectSink(device);
151        }
152
153        @Override
154        public int getConnectionStatus(BluetoothDevice device) {
155            return convertState(mService.getSinkState(device));
156        }
157
158        @Override
159        public int getSummary(BluetoothDevice device) {
160            int connectionStatus = getConnectionStatus(device);
161
162            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
163                return R.string.bluetooth_a2dp_profile_summary_connected;
164            } else {
165                return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
166            }
167        }
168
169        @Override
170        public boolean isPreferred(BluetoothDevice device) {
171            return mService.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
172        }
173
174        @Override
175        public void setPreferred(BluetoothDevice device, boolean preferred) {
176            mService.setSinkPriority(device,
177                    preferred ? BluetoothA2dp.PRIORITY_AUTO : BluetoothA2dp.PRIORITY_OFF);
178        }
179
180        @Override
181        public int convertState(int a2dpState) {
182            switch (a2dpState) {
183            case BluetoothA2dp.STATE_CONNECTED:
184                return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
185            case BluetoothA2dp.STATE_CONNECTING:
186                return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
187            case BluetoothA2dp.STATE_DISCONNECTED:
188                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
189            case BluetoothA2dp.STATE_DISCONNECTING:
190                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING;
191            case BluetoothA2dp.STATE_PLAYING:
192                return SettingsBtStatus.CONNECTION_STATUS_ACTIVE;
193            default:
194                return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
195            }
196        }
197    }
198
199    /**
200     * HeadsetProfileManager is an abstraction for the {@link BluetoothHeadset} service.
201     */
202    private static class HeadsetProfileManager extends LocalBluetoothProfileManager
203            implements BluetoothHeadset.ServiceListener {
204        private BluetoothHeadset mService;
205        private Handler mUiHandler = new Handler();
206
207        public HeadsetProfileManager(LocalBluetoothManager localManager) {
208            super(localManager);
209            mService = new BluetoothHeadset(localManager.getContext(), this);
210        }
211
212        public void onServiceConnected() {
213            // This could be called on a non-UI thread, funnel to UI thread.
214            mUiHandler.post(new Runnable() {
215                public void run() {
216                    /*
217                     * We just bound to the service, so refresh the UI of the
218                     * headset device.
219                     */
220                    BluetoothDevice device = mService.getCurrentHeadset();
221                    if (device == null) return;
222                    mLocalManager.getCachedDeviceManager()
223                            .onProfileStateChanged(device, Profile.HEADSET,
224                                                   BluetoothHeadset.STATE_CONNECTED);
225                }
226            });
227        }
228
229        public void onServiceDisconnected() {
230        }
231
232        @Override
233        public int connect(BluetoothDevice device) {
234            // Since connectHeadset fails if already connected to a headset, we
235            // disconnect from any headset first
236            mService.disconnectHeadset();
237            return mService.connectHeadset(device)
238                    ? BluetoothError.SUCCESS : BluetoothError.ERROR;
239        }
240
241        @Override
242        public int disconnect(BluetoothDevice device) {
243            if (mService.getCurrentHeadset().equals(device)) {
244                return mService.disconnectHeadset() ? BluetoothError.SUCCESS : BluetoothError.ERROR;
245            } else {
246                return BluetoothError.SUCCESS;
247            }
248        }
249
250        @Override
251        public int getConnectionStatus(BluetoothDevice device) {
252            BluetoothDevice currentDevice = mService.getCurrentHeadset();
253            return currentDevice != null && currentDevice.equals(device)
254                    ? convertState(mService.getState())
255                    : SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
256        }
257
258        @Override
259        public int getSummary(BluetoothDevice device) {
260            int connectionStatus = getConnectionStatus(device);
261
262            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
263                return R.string.bluetooth_headset_profile_summary_connected;
264            } else {
265                return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
266            }
267        }
268
269        @Override
270        public boolean isPreferred(BluetoothDevice device) {
271            return mService.getPriority(device) > BluetoothHeadset.PRIORITY_OFF;
272        }
273
274        @Override
275        public void setPreferred(BluetoothDevice device, boolean preferred) {
276            mService.setPriority(device,
277                    preferred ? BluetoothHeadset.PRIORITY_AUTO : BluetoothHeadset.PRIORITY_OFF);
278        }
279
280        @Override
281        public int convertState(int headsetState) {
282            switch (headsetState) {
283            case BluetoothHeadset.STATE_CONNECTED:
284                return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
285            case BluetoothHeadset.STATE_CONNECTING:
286                return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
287            case BluetoothHeadset.STATE_DISCONNECTED:
288                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
289            default:
290                return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
291            }
292        }
293    }
294}
295