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