HeadsetService.java revision 6c91bc0a163cc7600c40d7fb979777fd911d1ef1
1/*
2 * Copyright (C) 2012 Google Inc.
3 */
4
5package com.android.bluetooth.hfp;
6
7import android.app.Service;
8import android.bluetooth.BluetoothAdapter;
9import android.bluetooth.BluetoothDevice;
10import android.bluetooth.BluetoothProfile;
11import android.bluetooth.IBluetoothHeadset;
12import android.content.BroadcastReceiver;
13import android.content.Context;
14import android.content.Intent;
15import android.content.IntentFilter;
16import android.media.AudioManager;
17import android.os.AsyncResult;
18import android.os.Handler;
19import android.os.IBinder;
20import android.os.Message;
21import android.provider.Settings;
22import android.util.Log;
23import java.util.List;
24
25/**
26 * Provides Bluetooth Headset and Handsfree profile, as a service in
27 * the Bluetooth application.
28 * @hide
29 */
30public class HeadsetService extends Service {
31    private static final String TAG = "BluetoothHeadsetService";
32    private static final boolean DBG = true;
33
34    static final String BLUETOOTH_ADMIN_PERM =
35        android.Manifest.permission.BLUETOOTH_ADMIN;
36    static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
37
38    private BluetoothAdapter mAdapter;
39    private HeadsetStateMachine mStateMachine;
40
41    @Override
42    public void onCreate() {
43        log("onCreate");
44        super.onCreate();
45        mAdapter = BluetoothAdapter.getDefaultAdapter();
46        if (mAdapter == null) {
47            // no bluetooth on this device
48            return;
49        }
50        mStateMachine = new HeadsetStateMachine(this);
51        mStateMachine.start();
52        IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
53        filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
54        registerReceiver(mHeadsetReceiver, filter);
55    }
56
57    @Override
58    public IBinder onBind(Intent intent) {
59        log("onBind");
60        return mBinder;
61    }
62
63    public void onStart(Intent intent, int startId) {
64        log("onStart");
65        if (mAdapter == null) {
66            Log.w(TAG, "Stopping Bluetooth HeadsetService: device does not have BT");
67            stopSelf();
68        }
69    }
70
71    @Override
72    public void onDestroy() {
73        super.onDestroy();
74        if (DBG) log("Stopping Bluetooth HeadsetService");
75        unregisterReceiver(mHeadsetReceiver);
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
83            if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
84                mStateMachine.sendMessage(HeadsetStateMachine.INTENT_BATTERY_CHANGED, intent);
85            } else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
86                int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
87                if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) {
88                    mStateMachine.sendMessage(HeadsetStateMachine.INTENT_SCO_VOLUME_CHANGED,
89                                              intent);
90                }
91            }
92        }
93    };
94
95    /**
96     * Handlers for incoming service calls
97     */
98    private final IBluetoothHeadset.Stub mBinder = new IBluetoothHeadset.Stub() {
99
100        public boolean connect(BluetoothDevice device) {
101            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
102                                           "Need BLUETOOTH ADMIN permission");
103
104            if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
105                return false;
106            }
107
108            int connectionState = mStateMachine.getConnectionState(device);
109            if (connectionState == BluetoothProfile.STATE_CONNECTED ||
110                connectionState == BluetoothProfile.STATE_CONNECTING) {
111                return false;
112            }
113
114            mStateMachine.sendMessage(HeadsetStateMachine.CONNECT, device);
115            return true;
116        }
117
118        public boolean disconnect(BluetoothDevice device) {
119            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
120                                           "Need BLUETOOTH ADMIN permission");
121            int connectionState = mStateMachine.getConnectionState(device);
122            if (connectionState != BluetoothProfile.STATE_CONNECTED &&
123                connectionState != BluetoothProfile.STATE_CONNECTING) {
124                return false;
125            }
126
127            mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT, device);
128            return true;
129        }
130
131        public List<BluetoothDevice> getConnectedDevices() {
132            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
133            return mStateMachine.getConnectedDevices();
134        }
135
136        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
137            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
138            return mStateMachine.getDevicesMatchingConnectionStates(states);
139        }
140
141        public int getConnectionState(BluetoothDevice device) {
142            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
143            return mStateMachine.getConnectionState(device);
144        }
145
146        public boolean setPriority(BluetoothDevice device, int priority) {
147            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
148                                           "Need BLUETOOTH_ADMIN permission");
149            Settings.Secure.putInt(getContentResolver(),
150                Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()),
151                priority);
152            if (DBG) log("Saved priority " + device + " = " + priority);
153            return true;
154        }
155
156        public int getPriority(BluetoothDevice device) {
157            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
158                                           "Need BLUETOOTH_ADMIN permission");
159            int priority = Settings.Secure.getInt(getContentResolver(),
160                Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()),
161                BluetoothProfile.PRIORITY_UNDEFINED);
162            return priority;
163        }
164
165        public boolean startVoiceRecognition(BluetoothDevice device) {
166            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
167            int connectionState = mStateMachine.getConnectionState(device);
168            if (connectionState != BluetoothProfile.STATE_CONNECTED &&
169                connectionState != BluetoothProfile.STATE_CONNECTING) {
170                return false;
171            }
172            mStateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START);
173            return true;
174        }
175
176        public boolean stopVoiceRecognition(BluetoothDevice device) {
177            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
178            // It seem that we really need to check the AudioOn state.
179            // But since we allow startVoiceRecognition in STATE_CONNECTED and
180            // STATE_CONNECTING state, we do these 2 in this method
181            int connectionState = mStateMachine.getConnectionState(device);
182            if (connectionState != BluetoothProfile.STATE_CONNECTED &&
183                connectionState != BluetoothProfile.STATE_CONNECTING) {
184                return false;
185            }
186            mStateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP);
187            // TODO is this return correct when the voice recognition is not on?
188            return true;
189        }
190
191        public boolean isAudioOn() {
192            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
193            return mStateMachine.isAudioOn();
194        }
195
196        public boolean isAudioConnected(BluetoothDevice device) {
197            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
198            return mStateMachine.isAudioConnected(device);
199        }
200
201        public int getBatteryUsageHint(BluetoothDevice device) {
202            // TODO(BT) ask for BT stack support?
203            return 0;
204        }
205
206        public boolean acceptIncomingConnect(BluetoothDevice device) {
207            // TODO(BT) remove it if stack does access control
208            return false;
209        }
210
211        public boolean rejectIncomingConnect(BluetoothDevice device) {
212            // TODO(BT) remove it if stack does access control
213            return false;
214        }
215
216        public int getAudioState(BluetoothDevice device) {
217            return mStateMachine.getAudioState(device);
218        }
219
220        public boolean connectAudio() {
221            // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission
222            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
223            if (!mStateMachine.isConnected()) {
224                return false;
225            }
226            if (mStateMachine.isAudioOn()) {
227                return false;
228            }
229            mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO);
230            return true;
231        }
232
233        public boolean disconnectAudio() {
234            // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission
235            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
236            if (!mStateMachine.isAudioOn()) {
237                return false;
238            }
239            mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO);
240            return true;
241        }
242
243        public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
244            // TODO(BT) Is this right?
245            mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device);
246            return true;
247        }
248
249        public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
250            // TODO(BT) Is this right?
251            mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device);
252            return true;
253        }
254
255        public void phoneStateChanged(int numActive, int numHeld, int callState,
256                                      String number, int type) {
257            mStateMachine.sendMessage(HeadsetStateMachine.CALL_STATE_CHANGED,
258                new HeadsetCallState(numActive, numHeld, callState, number, type));
259        }
260
261        public void roamChanged(boolean roam) {
262            mStateMachine.sendMessage(HeadsetStateMachine.ROAM_CHANGED, roam);
263        }
264
265        public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
266                                 String number, int type) {
267            mStateMachine.sendMessage(HeadsetStateMachine.SEND_CCLC_RESPONSE,
268                new HeadsetClccResponse(index, direction, status, mode, mpty, number, type));
269        }
270    };
271
272    private static void log(String msg) {
273        Log.d(TAG, msg);
274    }
275}
276