Merge "Change the routing path of bluetooth headset connections." into lmp-mr1-dev

This commit is contained in:
Benjamin Franz
2014-12-05 12:04:00 +00:00
committed by Android (Google) Code Review
5 changed files with 325 additions and 38 deletions

View File

@@ -100,6 +100,7 @@ LOCAL_SRC_FILES += \
core/java/android/bluetooth/IBluetoothA2dpSink.aidl \
core/java/android/bluetooth/IBluetoothAvrcpController.aidl \
core/java/android/bluetooth/IBluetoothCallback.aidl \
core/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl \
core/java/android/bluetooth/IBluetoothHeadset.aidl \
core/java/android/bluetooth/IBluetoothHeadsetPhone.aidl \
core/java/android/bluetooth/IBluetoothHealth.aidl \

View File

@@ -20,9 +20,10 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
@@ -221,11 +222,14 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public static final int STATE_AUDIO_CONNECTED = 12;
private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100;
private static final int MESSAGE_HEADSET_SERVICE_DISCONNECTED = 101;
private Context mContext;
private ServiceListener mServiceListener;
private IBluetoothHeadset mService;
private BluetoothAdapter mAdapter;
private boolean mIsClosed;
final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
new IBluetoothStateChangeCallback.Stub() {
@@ -233,14 +237,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
if (!up) {
if (VDBG) Log.d(TAG,"Unbinding service...");
synchronized (mConnection) {
try {
mService = null;
mContext.unbindService(mConnection);
} catch (Exception re) {
Log.e(TAG,"",re);
}
}
doUnbind();
} else {
synchronized (mConnection) {
try {
@@ -263,6 +260,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
mContext = context;
mServiceListener = l;
mAdapter = BluetoothAdapter.getDefaultAdapter();
mIsClosed = false;
IBluetoothManager mgr = mAdapter.getBluetoothManager();
if (mgr != null) {
@@ -277,15 +275,26 @@ public final class BluetoothHeadset implements BluetoothProfile {
}
boolean doBind() {
Intent intent = new Intent(IBluetoothHeadset.class.getName());
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
android.os.Process.myUserHandle())) {
Log.e(TAG, "Could not bind to Bluetooth Headset Service with " + intent);
return false;
try {
return mAdapter.getBluetoothManager().bindBluetoothProfileService(
BluetoothProfile.HEADSET, mConnection);
} catch (RemoteException e) {
Log.e(TAG, "Unable to bind HeadsetService", e);
}
return false;
}
void doUnbind() {
synchronized (mConnection) {
if (mService != null) {
try {
mAdapter.getBluetoothManager().unbindBluetoothProfileService(
BluetoothProfile.HEADSET, mConnection);
} catch (RemoteException e) {
Log.e(TAG,"Unable to unbind HeadsetService", e);
}
}
}
return true;
}
/**
@@ -305,18 +314,8 @@ public final class BluetoothHeadset implements BluetoothProfile {
Log.e(TAG,"",e);
}
}
synchronized (mConnection) {
if (mService != null) {
try {
mService = null;
mContext.unbindService(mConnection);
} catch (Exception re) {
Log.e(TAG,"",re);
}
}
}
mServiceListener = null;
mIsClosed = true;
doUnbind();
}
/**
@@ -930,21 +929,21 @@ public final class BluetoothHeadset implements BluetoothProfile {
return false;
}
private final ServiceConnection mConnection = new ServiceConnection() {
private final IBluetoothProfileServiceConnection mConnection
= new IBluetoothProfileServiceConnection.Stub() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
mService = IBluetoothHeadset.Stub.asInterface(service);
if (mServiceListener != null) {
mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, BluetoothHeadset.this);
}
mHandler.sendMessage(mHandler.obtainMessage(
MESSAGE_HEADSET_SERVICE_CONNECTED));
}
@Override
public void onServiceDisconnected(ComponentName className) {
if (DBG) Log.d(TAG, "Proxy object disconnected");
mService = null;
if (mServiceListener != null) {
mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET);
}
mHandler.sendMessage(mHandler.obtainMessage(
MESSAGE_HEADSET_SERVICE_DISCONNECTED));
}
};
@@ -968,4 +967,28 @@ public final class BluetoothHeadset implements BluetoothProfile {
private static void log(String msg) {
Log.d(TAG, msg);
}
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_HEADSET_SERVICE_CONNECTED: {
if (mServiceListener != null) {
mServiceListener.onServiceConnected(BluetoothProfile.HEADSET,
BluetoothHeadset.this);
}
break;
}
case MESSAGE_HEADSET_SERVICE_DISCONNECTED: {
if (mServiceListener != null) {
mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET);
}
if (mIsClosed){
mServiceListener = null;
}
break;
}
}
}
};
}

View File

@@ -19,6 +19,7 @@ package android.bluetooth;
import android.bluetooth.IBluetooth;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothManagerCallback;
import android.bluetooth.IBluetoothProfileServiceConnection;
import android.bluetooth.IBluetoothStateChangeCallback;
/**
@@ -38,6 +39,9 @@ interface IBluetoothManager
boolean disable(boolean persist);
IBluetoothGatt getBluetoothGatt();
boolean bindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy);
void unbindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy);
String getAddress();
String getName();
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.bluetooth;
import android.content.ComponentName;
import android.os.IBinder;
/**
* Callback for bluetooth profile connections.
*
* {@hide}
*/
interface IBluetoothProfileServiceConnection {
void onServiceConnected(in ComponentName comp, in IBinder service);
void onServiceDisconnected(in ComponentName comp);
}

View File

@@ -18,11 +18,14 @@ package com.android.server;
import android.app.ActivityManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetooth;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothCallback;
import android.bluetooth.IBluetoothHeadset;
import android.bluetooth.IBluetoothManager;
import android.bluetooth.IBluetoothManagerCallback;
import android.bluetooth.IBluetoothProfileServiceConnection;
import android.bluetooth.IBluetoothStateChangeCallback;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -32,6 +35,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -42,12 +46,18 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.Vector;
class BluetoothManagerService extends IBluetoothManager.Stub {
private static final String TAG = "BluetoothManagerService";
private static final boolean DBG = true;
@@ -67,6 +77,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
private static final int ERROR_RESTART_TIME_MS = 3000;
//Maximum msec to delay MESSAGE_USER_SWITCHED
private static final int USER_SWITCHED_TIME_MS = 200;
// Delay for the addProxy function in msec
private static final int ADD_PROXY_DELAY_MS = 100;
private static final int MESSAGE_ENABLE = 1;
private static final int MESSAGE_DISABLE = 2;
@@ -83,6 +95,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
private static final int MESSAGE_GET_NAME_AND_ADDRESS=200;
private static final int MESSAGE_SAVE_NAME_AND_ADDRESS=201;
private static final int MESSAGE_USER_SWITCHED = 300;
private static final int MESSAGE_ADD_PROXY_DELAYED = 400;
private static final int MESSAGE_BIND_PROFILE_SERVICE = 401;
private static final int MAX_SAVE_RETRIES=3;
private static final int MAX_ERROR_RESTART_RETRIES=6;
@@ -127,6 +141,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
private int mErrorRecoveryRetryCounter;
private final int mSystemUiUid;
// Save a ProfileServiceConnections object for each of the bound
// bluetooth profile services
private final Map <Integer, ProfileServiceConnections> mProfileServices =
new HashMap <Integer, ProfileServiceConnections>();
private void registerForAirplaneMode(IntentFilter filter) {
final ContentResolver resolver = mContext.getContentResolver();
final String airplaneModeRadios = Settings.Global.getString(resolver,
@@ -499,6 +518,187 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
return mBluetoothGatt;
}
@Override
public boolean bindBluetoothProfileService(int bluetoothProfile,
IBluetoothProfileServiceConnection proxy) {
if (!mEnable) {
if (DBG) {
Log.d(TAG, "Trying to bind to profile: " + bluetoothProfile +
", while Bluetooth was disabled");
}
return false;
}
synchronized (mProfileServices) {
ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile));
if (psc == null) {
if (DBG) {
Log.d(TAG, "Creating new ProfileServiceConnections object for"
+ " profile: " + bluetoothProfile);
}
Intent intent = null;
if (bluetoothProfile == BluetoothProfile.HEADSET) {
intent = new Intent(IBluetoothHeadset.class.getName());
} else {
return false;
}
psc = new ProfileServiceConnections(intent);
mProfileServices.put(new Integer(bluetoothProfile), psc);
psc.bindService();
}
}
// Introducing a delay to give the client app time to prepare
Message addProxyMsg = mHandler.obtainMessage(MESSAGE_ADD_PROXY_DELAYED);
addProxyMsg.arg1 = bluetoothProfile;
addProxyMsg.obj = proxy;
mHandler.sendMessageDelayed(addProxyMsg, ADD_PROXY_DELAY_MS);
return true;
}
@Override
public void unbindBluetoothProfileService(int bluetoothProfile,
IBluetoothProfileServiceConnection proxy) {
synchronized (mProfileServices) {
ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile));
if (psc == null) {
return;
}
psc.removeProxy(proxy);
}
}
private void unbindAllBluetoothProfileServices() {
synchronized (mProfileServices) {
for (Integer i : mProfileServices.keySet()) {
ProfileServiceConnections psc = mProfileServices.get(i);
mContext.unbindService(psc);
psc.removeAllProxies();
}
mProfileServices.clear();
}
}
/**
* This class manages the clients connected to a given ProfileService
* and maintains the connection with that service.
*/
final private class ProfileServiceConnections implements ServiceConnection,
IBinder.DeathRecipient {
final RemoteCallbackList<IBluetoothProfileServiceConnection> mProxies =
new RemoteCallbackList <IBluetoothProfileServiceConnection>();
IBinder mService;
ComponentName mClassName;
Intent mIntent;
ProfileServiceConnections(Intent intent) {
mService = null;
mClassName = null;
mIntent = intent;
}
private void bindService() {
if (mIntent != null && mService == null) {
if (!doBind(mIntent, this, 0, UserHandle.CURRENT_OR_SELF)) {
Log.w(TAG, "Unable to bind with intent: " + mIntent
+ ". Triggering retry.");
}
Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
msg.obj = this;
mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS);
}
}
private void addProxy(IBluetoothProfileServiceConnection proxy) {
mProxies.register(proxy);
if (mService != null) {
try{
proxy.onServiceConnected(mClassName, mService);
} catch (RemoteException e) {
Log.e(TAG, "Unable to connect to proxy", e);
}
} else {
if (!mHandler.hasMessages(MESSAGE_BIND_PROFILE_SERVICE, this)) {
Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
msg.obj = this;
mHandler.sendMessage(msg);
}
}
}
private void removeProxy(IBluetoothProfileServiceConnection proxy) {
if (proxy != null) {
if (mProxies.unregister(proxy)) {
try {
proxy.onServiceDisconnected(mClassName);
} catch (RemoteException e) {
Log.e(TAG, "Unable to disconnect proxy", e);
}
}
} else {
Log.w(TAG, "Trying to remove a null proxy");
}
}
private void removeAllProxies() {
onServiceDisconnected(mClassName);
mProxies.kill();
}
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
// remove timeout message
mHandler.removeMessages(MESSAGE_BIND_PROFILE_SERVICE, this);
mService = service;
mClassName = className;
try {
mService.linkToDeath(this, 0);
} catch (RemoteException e) {
Log.e(TAG, "Unable to linkToDeath", e);
}
int n = mProxies.beginBroadcast();
for (int i = 0; i < n; i++) {
try {
mProxies.getBroadcastItem(i).onServiceConnected(className, service);
} catch (RemoteException e) {
Log.e(TAG, "Unable to connect to proxy", e);
}
}
mProxies.finishBroadcast();
}
@Override
public void onServiceDisconnected(ComponentName className) {
if (mService == null) {
return;
}
mService.unlinkToDeath(this, 0);
mService = null;
mClassName = null;
int n = mProxies.beginBroadcast();
for (int i = 0; i < n; i++) {
try {
mProxies.getBroadcastItem(i).onServiceDisconnected(className);
} catch (RemoteException e) {
Log.e(TAG, "Unable to disconnect from proxy", e);
}
}
mProxies.finishBroadcast();
}
@Override
public void binderDied() {
if (DBG) {
Log.w(TAG, "Profile service for profile: " + mClassName
+ " died.");
}
onServiceDisconnected(mClassName);
// Trigger rebind
Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
msg.obj = this;
mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS);
}
}
private void sendBluetoothStateCallback(boolean isUp) {
int n = mStateChangeCallbacks.beginBroadcast();
if (DBG) Log.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers.");
@@ -803,6 +1003,28 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
}
break;
}
case MESSAGE_ADD_PROXY_DELAYED:
{
ProfileServiceConnections psc = mProfileServices.get(
new Integer(msg.arg1));
if (psc == null) {
break;
}
IBluetoothProfileServiceConnection proxy =
(IBluetoothProfileServiceConnection) msg.obj;
psc.addProxy(proxy);
break;
}
case MESSAGE_BIND_PROFILE_SERVICE:
{
ProfileServiceConnections psc = (ProfileServiceConnections) msg.obj;
removeMessages(MESSAGE_BIND_PROFILE_SERVICE, msg.obj);
if (psc == null) {
break;
}
psc.bindService();
break;
}
case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
{
if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
@@ -1005,6 +1227,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON);
}
unbindAllBluetoothProfileServices();
// disable
handleDisable();
// Pbap service need receive STATE_TURNING_OFF intent to close
@@ -1129,16 +1352,21 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
int callingUser = UserHandle.getCallingUserId();
int callingUid = Binder.getCallingUid();
long callingIdentity = Binder.clearCallingIdentity();
UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
UserInfo ui = um.getProfileParent(callingUser);
int parentUser = (ui != null) ? ui.id : UserHandle.USER_NULL;
int callingAppId = UserHandle.getAppId(callingUid);
boolean valid = false;
try {
foregroundUser = ActivityManager.getCurrentUser();
valid = (callingUser == foregroundUser) ||
parentUser == foregroundUser ||
callingAppId == Process.NFC_UID ||
callingAppId == mSystemUiUid;
if (DBG) {
Log.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid
+ " callingUser=" + callingUser
+ " parentUser=" + parentUser
+ " foregroundUser=" + foregroundUser);
}
} finally {
@@ -1165,6 +1393,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
} else {
//If Bluetooth is off, send service down event to proxy objects, and unbind
if (!isUp && canUnbindBluetoothService()) {
unbindAllBluetoothProfileServices();
sendBluetoothServiceDownCallback();
unbindAndFinish();
}