1 /*
2  * Copyright (C) 2012 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.settingslib.bluetooth;
18 
19 import android.bluetooth.BluetoothAdapter;
20 import android.bluetooth.BluetoothClass;
21 import android.bluetooth.BluetoothDevice;
22 import android.bluetooth.BluetoothHeadset;
23 import android.bluetooth.BluetoothProfile;
24 import android.bluetooth.BluetoothUuid;
25 import android.content.Context;
26 import android.os.ParcelUuid;
27 import android.util.Log;
28 
29 import com.android.settingslib.R;
30 
31 import java.util.ArrayList;
32 import java.util.List;
33 
34 /**
35  * HeadsetProfile handles Bluetooth HFP and Headset profiles.
36  */
37 public class HeadsetProfile implements LocalBluetoothProfile {
38     private static final String TAG = "HeadsetProfile";
39 
40     private BluetoothHeadset mService;
41     private boolean mIsProfileReady;
42 
43     private final CachedBluetoothDeviceManager mDeviceManager;
44     private final LocalBluetoothProfileManager mProfileManager;
45 
46     static final ParcelUuid[] UUIDS = {
47         BluetoothUuid.HSP,
48         BluetoothUuid.Handsfree,
49     };
50 
51     static final String NAME = "HEADSET";
52 
53     // Order of this profile in device profiles list
54     private static final int ORDINAL = 0;
55 
56     // These callbacks run on the main thread.
57     private final class HeadsetServiceListener
58             implements BluetoothProfile.ServiceListener {
59 
onServiceConnected(int profile, BluetoothProfile proxy)60         public void onServiceConnected(int profile, BluetoothProfile proxy) {
61             mService = (BluetoothHeadset) proxy;
62             // We just bound to the service, so refresh the UI for any connected HFP devices.
63             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
64             while (!deviceList.isEmpty()) {
65                 BluetoothDevice nextDevice = deviceList.remove(0);
66                 CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
67                 // we may add a new device here, but generally this should not happen
68                 if (device == null) {
69                     Log.w(TAG, "HeadsetProfile found new device: " + nextDevice);
70                     device = mDeviceManager.addDevice(nextDevice);
71                 }
72                 device.onProfileStateChanged(HeadsetProfile.this,
73                         BluetoothProfile.STATE_CONNECTED);
74                 device.refresh();
75             }
76             mIsProfileReady=true;
77             mProfileManager.callServiceConnectedListeners();
78         }
79 
onServiceDisconnected(int profile)80         public void onServiceDisconnected(int profile) {
81             mProfileManager.callServiceDisconnectedListeners();
82             mIsProfileReady=false;
83         }
84     }
85 
isProfileReady()86     public boolean isProfileReady() {
87         return mIsProfileReady;
88     }
89 
90     @Override
getProfileId()91     public int getProfileId() {
92         return BluetoothProfile.HEADSET;
93     }
94 
HeadsetProfile(Context context, CachedBluetoothDeviceManager deviceManager, LocalBluetoothProfileManager profileManager)95     HeadsetProfile(Context context, CachedBluetoothDeviceManager deviceManager,
96             LocalBluetoothProfileManager profileManager) {
97         mDeviceManager = deviceManager;
98         mProfileManager = profileManager;
99         BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, new HeadsetServiceListener(),
100                 BluetoothProfile.HEADSET);
101     }
102 
accessProfileEnabled()103     public boolean accessProfileEnabled() {
104         return true;
105     }
106 
isAutoConnectable()107     public boolean isAutoConnectable() {
108         return true;
109     }
110 
connect(BluetoothDevice device)111     public boolean connect(BluetoothDevice device) {
112         if (mService == null) {
113             return false;
114         }
115         return mService.connect(device);
116     }
117 
disconnect(BluetoothDevice device)118     public boolean disconnect(BluetoothDevice device) {
119         if (mService == null) {
120             return false;
121         }
122         // Downgrade priority as user is disconnecting the headset.
123         if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
124             mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
125         }
126         return mService.disconnect(device);
127     }
128 
getConnectionStatus(BluetoothDevice device)129     public int getConnectionStatus(BluetoothDevice device) {
130         if (mService == null) {
131             return BluetoothProfile.STATE_DISCONNECTED;
132         }
133         return mService.getConnectionState(device);
134     }
135 
setActiveDevice(BluetoothDevice device)136     public boolean setActiveDevice(BluetoothDevice device) {
137         if (mService == null) {
138             return false;
139         }
140         return mService.setActiveDevice(device);
141     }
142 
getActiveDevice()143     public BluetoothDevice getActiveDevice() {
144         if (mService == null) {
145             return null;
146         }
147         return mService.getActiveDevice();
148     }
149 
isAudioOn()150     public boolean isAudioOn() {
151         if (mService == null) {
152             return false;
153         }
154         return mService.isAudioOn();
155     }
156 
getAudioState(BluetoothDevice device)157     public int getAudioState(BluetoothDevice device) {
158         if (mService == null) {
159             return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
160         }
161         return mService.getAudioState(device);
162     }
163 
isPreferred(BluetoothDevice device)164     public boolean isPreferred(BluetoothDevice device) {
165         if (mService == null) {
166             return false;
167         }
168         return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
169     }
170 
getPreferred(BluetoothDevice device)171     public int getPreferred(BluetoothDevice device) {
172         if (mService == null) {
173             return BluetoothProfile.PRIORITY_OFF;
174         }
175         return mService.getPriority(device);
176     }
177 
setPreferred(BluetoothDevice device, boolean preferred)178     public void setPreferred(BluetoothDevice device, boolean preferred) {
179         if (mService == null) {
180             return;
181         }
182         if (preferred) {
183             if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
184                 mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
185             }
186         } else {
187             mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
188         }
189     }
190 
getConnectedDevices()191     public List<BluetoothDevice> getConnectedDevices() {
192         if (mService == null) {
193             return new ArrayList<BluetoothDevice>(0);
194         }
195         return mService.getDevicesMatchingConnectionStates(
196               new int[] {BluetoothProfile.STATE_CONNECTED,
197                          BluetoothProfile.STATE_CONNECTING,
198                          BluetoothProfile.STATE_DISCONNECTING});
199     }
200 
toString()201     public String toString() {
202         return NAME;
203     }
204 
getOrdinal()205     public int getOrdinal() {
206         return ORDINAL;
207     }
208 
getNameResource(BluetoothDevice device)209     public int getNameResource(BluetoothDevice device) {
210         return R.string.bluetooth_profile_headset;
211     }
212 
getSummaryResourceForDevice(BluetoothDevice device)213     public int getSummaryResourceForDevice(BluetoothDevice device) {
214         int state = getConnectionStatus(device);
215         switch (state) {
216             case BluetoothProfile.STATE_DISCONNECTED:
217                 return R.string.bluetooth_headset_profile_summary_use_for;
218 
219             case BluetoothProfile.STATE_CONNECTED:
220                 return R.string.bluetooth_headset_profile_summary_connected;
221 
222             default:
223                 return BluetoothUtils.getConnectionStateSummary(state);
224         }
225     }
226 
getDrawableResource(BluetoothClass btClass)227     public int getDrawableResource(BluetoothClass btClass) {
228         return com.android.internal.R.drawable.ic_bt_headset_hfp;
229     }
230 
finalize()231     protected void finalize() {
232         Log.d(TAG, "finalize()");
233         if (mService != null) {
234             try {
235                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HEADSET,
236                                                                        mService);
237                 mService = null;
238             }catch (Throwable t) {
239                 Log.w(TAG, "Error cleaning up HID proxy", t);
240             }
241         }
242     }
243 }
244