HeadsetService.java revision 74ae04c73312403e89db0f8e9bd9601d403b4783
1/*
2 * Copyright (C) 2012 Google Inc.
3 */
4
5package com.android.bluetooth.hfp;
6
7import android.bluetooth.BluetoothDevice;
8import android.bluetooth.BluetoothHeadset;
9import android.bluetooth.BluetoothProfile;
10import android.bluetooth.IBluetoothHeadset;
11import android.content.BroadcastReceiver;
12import android.content.Context;
13import android.content.Intent;
14import android.content.IntentFilter;
15import android.media.AudioManager;
16import android.os.Handler;
17import android.os.Message;
18import android.provider.Settings;
19import android.util.Log;
20
21import java.util.ArrayList;
22import java.util.List;
23import java.util.Iterator;
24import java.util.Map;
25import android.content.pm.PackageManager;
26import com.android.bluetooth.btservice.ProfileService;
27
28/**
29 * Provides Bluetooth Headset and Handsfree profile, as a service in
30 * the Bluetooth application.
31 * @hide
32 */
33public class HeadsetService extends ProfileService {
34    private static final boolean DBG = true;
35    private static final String TAG = "HeadsetService";
36    private HeadsetStateMachine mStateMachine;
37
38    protected String getName() {
39        return TAG;
40    }
41
42    public IProfileServiceBinder initBinder() {
43        return new BluetoothHeadsetBinder(this);
44    }
45
46    protected boolean start() {
47        mStateMachine = new HeadsetStateMachine(this);
48        mStateMachine.start();
49        IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
50        filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
51        filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
52        try {
53            registerReceiver(mHeadsetReceiver, filter);
54        } catch (Exception e) {
55            Log.w(TAG,"Unable to register headset receiver",e);
56        }
57        return true;
58    }
59
60    protected boolean stop() {
61        try {
62            unregisterReceiver(mHeadsetReceiver);
63        } catch (Exception e) {
64            Log.w(TAG,"Unable to unregister headset receiver",e);
65        }
66        mStateMachine.quit();
67        return true;
68    }
69
70    protected boolean cleanup() {
71        if (mStateMachine != null) {
72            mStateMachine.cleanup();
73            mStateMachine=null;
74        }
75        return true;
76    }
77
78    private final BroadcastReceiver mHeadsetReceiver = new BroadcastReceiver() {
79        @Override
80        public void onReceive(Context context, Intent intent) {
81            String action = intent.getAction();
82            if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
83                mStateMachine.sendMessage(HeadsetStateMachine.INTENT_BATTERY_CHANGED, intent);
84            } else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
85                int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
86                if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) {
87                    mStateMachine.sendMessage(HeadsetStateMachine.INTENT_SCO_VOLUME_CHANGED,
88                                              intent);
89                }
90            }
91            else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
92                Log.v(TAG, "HeadsetService -  Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY");
93                mStateMachine.handleAccessPermissionResult(intent);
94            }
95        }
96    };
97
98    /**
99     * Handlers for incoming service calls
100     */
101    private static class BluetoothHeadsetBinder extends IBluetoothHeadset.Stub implements IProfileServiceBinder {
102        private HeadsetService mService;
103
104        public BluetoothHeadsetBinder(HeadsetService svc) {
105            mService = svc;
106        }
107        public boolean cleanup() {
108            mService = null;
109            return true;
110        }
111
112        private HeadsetService getService() {
113            if (mService  != null && mService.isAvailable()) {
114                return mService;
115            }
116            return null;
117        }
118
119        public boolean connect(BluetoothDevice device) {
120            HeadsetService service = getService();
121            if (service == null) return false;
122            return service.connect(device);
123        }
124
125        public boolean disconnect(BluetoothDevice device) {
126            HeadsetService service = getService();
127            if (service == null) return false;
128            return service.disconnect(device);
129        }
130
131        public List<BluetoothDevice> getConnectedDevices() {
132            HeadsetService service = getService();
133            if (service == null) return new ArrayList<BluetoothDevice>(0);
134            return service.getConnectedDevices();
135        }
136
137        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
138            HeadsetService service = getService();
139            if (service == null) return new ArrayList<BluetoothDevice>(0);
140            return service.getDevicesMatchingConnectionStates(states);
141        }
142
143        public int getConnectionState(BluetoothDevice device) {
144            HeadsetService service = getService();
145            if (service == null) return BluetoothProfile.STATE_DISCONNECTED;
146            return service.getConnectionState(device);
147        }
148
149        public boolean setPriority(BluetoothDevice device, int priority) {
150            HeadsetService service = getService();
151            if (service == null) return false;
152            return service.setPriority(device, priority);
153        }
154
155        public int getPriority(BluetoothDevice device) {
156            HeadsetService service = getService();
157            if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED;
158            return service.getPriority(device);
159        }
160
161        public boolean startVoiceRecognition(BluetoothDevice device) {
162            HeadsetService service = getService();
163            if (service == null) return false;
164            return service.startVoiceRecognition(device);
165        }
166
167        public boolean stopVoiceRecognition(BluetoothDevice device) {
168            HeadsetService service = getService();
169            if (service == null) return false;
170            return service.stopVoiceRecognition(device);
171        }
172
173        public boolean isAudioOn() {
174            HeadsetService service = getService();
175            if (service == null) return false;
176            return service.isAudioOn();
177        }
178
179        public boolean isAudioConnected(BluetoothDevice device) {
180            HeadsetService service = getService();
181            if (service == null) return false;
182            return service.isAudioConnected(device);
183        }
184
185        public int getBatteryUsageHint(BluetoothDevice device) {
186            HeadsetService service = getService();
187            if (service == null) return 0;
188            return service.getBatteryUsageHint(device);
189        }
190
191        public boolean acceptIncomingConnect(BluetoothDevice device) {
192            HeadsetService service = getService();
193            if (service == null) return false;
194            return service.acceptIncomingConnect(device);
195        }
196
197        public boolean rejectIncomingConnect(BluetoothDevice device) {
198            HeadsetService service = getService();
199            if (service == null) return false;
200            return service.rejectIncomingConnect(device);
201        }
202
203        public int getAudioState(BluetoothDevice device) {
204            HeadsetService service = getService();
205            if (service == null) return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
206            return service.getAudioState(device);
207        }
208
209        public boolean connectAudio() {
210            HeadsetService service = getService();
211            if (service == null) return false;
212            return service.connectAudio();
213        }
214
215        public boolean disconnectAudio() {
216            HeadsetService service = getService();
217            if (service == null) return false;
218            return service.disconnectAudio();
219        }
220
221        public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
222            HeadsetService service = getService();
223            if (service == null) return false;
224            return service.startScoUsingVirtualVoiceCall(device);
225        }
226
227        public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
228            HeadsetService service = getService();
229            if (service == null) return false;
230            return service.stopScoUsingVirtualVoiceCall(device);
231        }
232
233        public void phoneStateChanged(int numActive, int numHeld, int callState,
234                                      String number, int type) {
235            HeadsetService service = getService();
236            if (service == null) return;
237            service.phoneStateChanged(numActive, numHeld, callState, number, type);
238        }
239
240        public void roamChanged(boolean roam) {
241            HeadsetService service = getService();
242            if (service == null) return;
243            service.roamChanged(roam);
244        }
245
246        public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
247                                 String number, int type) {
248            HeadsetService service = getService();
249            if (service == null) return;
250            service.clccResponse(index, direction, status, mode, mpty, number, type);
251        }
252    };
253
254    //API methods
255    boolean connect(BluetoothDevice device) {
256        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
257                                       "Need BLUETOOTH ADMIN permission");
258
259        if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
260            return false;
261        }
262
263        int connectionState = mStateMachine.getConnectionState(device);
264        if (connectionState == BluetoothProfile.STATE_CONNECTED ||
265            connectionState == BluetoothProfile.STATE_CONNECTING) {
266            return false;
267        }
268
269        mStateMachine.sendMessage(HeadsetStateMachine.CONNECT, device);
270        return true;
271    }
272
273    boolean disconnect(BluetoothDevice device) {
274        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
275                                       "Need BLUETOOTH ADMIN permission");
276        int connectionState = mStateMachine.getConnectionState(device);
277        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
278            connectionState != BluetoothProfile.STATE_CONNECTING) {
279            return false;
280        }
281
282        mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT, device);
283        return true;
284    }
285
286    List<BluetoothDevice> getConnectedDevices() {
287        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
288        return mStateMachine.getConnectedDevices();
289    }
290
291    private List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
292        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
293        return mStateMachine.getDevicesMatchingConnectionStates(states);
294    }
295
296    int getConnectionState(BluetoothDevice device) {
297        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
298        return mStateMachine.getConnectionState(device);
299    }
300
301    boolean setPriority(BluetoothDevice device, int priority) {
302        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
303                                       "Need BLUETOOTH_ADMIN permission");
304        Settings.Secure.putInt(getContentResolver(),
305            Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()),
306            priority);
307        if (DBG) Log.d(TAG, "Saved priority " + device + " = " + priority);
308        return true;
309    }
310
311    int getPriority(BluetoothDevice device) {
312        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
313                                       "Need BLUETOOTH_ADMIN permission");
314        int priority = Settings.Secure.getInt(getContentResolver(),
315            Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()),
316            BluetoothProfile.PRIORITY_UNDEFINED);
317        return priority;
318    }
319
320    boolean startVoiceRecognition(BluetoothDevice device) {
321        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
322        int connectionState = mStateMachine.getConnectionState(device);
323        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
324            connectionState != BluetoothProfile.STATE_CONNECTING) {
325            return false;
326        }
327        mStateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START);
328        return true;
329    }
330
331    boolean stopVoiceRecognition(BluetoothDevice device) {
332        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
333        // It seem that we really need to check the AudioOn state.
334        // But since we allow startVoiceRecognition in STATE_CONNECTED and
335        // STATE_CONNECTING state, we do these 2 in this method
336        int connectionState = mStateMachine.getConnectionState(device);
337        if (connectionState != BluetoothProfile.STATE_CONNECTED &&
338            connectionState != BluetoothProfile.STATE_CONNECTING) {
339            return false;
340        }
341        mStateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP);
342        // TODO is this return correct when the voice recognition is not on?
343        return true;
344    }
345
346    boolean isAudioOn() {
347        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
348        return mStateMachine.isAudioOn();
349    }
350
351    boolean isAudioConnected(BluetoothDevice device) {
352        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
353        return mStateMachine.isAudioConnected(device);
354    }
355
356    int getBatteryUsageHint(BluetoothDevice device) {
357        // TODO(BT) ask for BT stack support?
358        return 0;
359    }
360
361    boolean acceptIncomingConnect(BluetoothDevice device) {
362        // TODO(BT) remove it if stack does access control
363        return false;
364    }
365
366    boolean rejectIncomingConnect(BluetoothDevice device) {
367        // TODO(BT) remove it if stack does access control
368        return false;
369    }
370
371    int getAudioState(BluetoothDevice device) {
372        return mStateMachine.getAudioState(device);
373    }
374
375    boolean connectAudio() {
376        // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission
377        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
378        if (!mStateMachine.isConnected()) {
379            return false;
380        }
381        if (mStateMachine.isAudioOn()) {
382            return false;
383        }
384        mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO);
385        return true;
386    }
387
388    boolean disconnectAudio() {
389        // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission
390        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
391        if (!mStateMachine.isAudioOn()) {
392            return false;
393        }
394        mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO);
395        return true;
396    }
397
398    boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
399        // TODO(BT) Is this right?
400        mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device);
401        return true;
402    }
403
404    boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
405        // TODO(BT) Is this right?
406        mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device);
407        return true;
408    }
409
410    private void phoneStateChanged(int numActive, int numHeld, int callState,
411                                  String number, int type) {
412        mStateMachine.sendMessage(HeadsetStateMachine.CALL_STATE_CHANGED,
413            new HeadsetCallState(numActive, numHeld, callState, number, type));
414    }
415
416    private void roamChanged(boolean roam) {
417        mStateMachine.sendMessage(HeadsetStateMachine.ROAM_CHANGED, roam);
418    }
419
420    private void clccResponse(int index, int direction, int status, int mode, boolean mpty,
421                             String number, int type) {
422        mStateMachine.sendMessage(HeadsetStateMachine.SEND_CCLC_RESPONSE,
423            new HeadsetClccResponse(index, direction, status, mode, mpty, number, type));
424    }
425
426}
427