android_frameworks_base/services/java/com/android/server/BluetoothManagerService.java
Zhihai Xu d31c32217c Fix for BluetoothAdapter.getAddress() when BT is off on some devices
There are two problem, 1.If we have wrong bluetooth address in
global settings db, we never will update it 2. We need enable bluetooth
to get the bluetooth address for some devices. For 1, we fix it by add
a valid flag in global setting db, this flag will be set when we stored
correct address and name to db. We only load the name and address from
global setting db when this valid flag is set during power up.
For2. we will read BT address after bluetooth is at ON state.
bug 7440409

Change-Id: Ic4740b3f0b2fcd214c7ca8393f7331c140eec66d
2012-11-01 02:19:08 -07:00

1041 lines
43 KiB
Java
Executable File

/*
* Copyright (C) 2012 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 com.android.server;
import android.app.ActivityManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.IBluetooth;
import android.bluetooth.IBluetoothCallback;
import android.bluetooth.IBluetoothManager;
import android.bluetooth.IBluetoothManagerCallback;
import android.bluetooth.IBluetoothStateChangeCallback;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
class BluetoothManagerService extends IBluetoothManager.Stub {
private static final String TAG = "BluetoothManagerService";
private static final boolean DBG = true;
private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
private static final String ACTION_SERVICE_STATE_CHANGED="com.android.bluetooth.btservice.action.STATE_CHANGED";
private static final String EXTRA_ACTION="action";
private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID="bluetooth_addr_valid";
private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS="bluetooth_address";
private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name";
private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind
private static final int TIMEOUT_SAVE_MS = 500; //Maximum msec to wait for a save
//Maximum msec to wait for service restart
private static final int SERVICE_RESTART_TIME_MS = 200;
//Maximum msec to delay MESSAGE_USER_SWITCHED
private static final int USER_SWITCHED_TIME_MS = 200;
private static final int MESSAGE_ENABLE = 1;
private static final int MESSAGE_DISABLE = 2;
private static final int MESSAGE_REGISTER_ADAPTER = 20;
private static final int MESSAGE_UNREGISTER_ADAPTER = 21;
private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30;
private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31;
private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40;
private static final int MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED = 41;
private static final int MESSAGE_RESTART_BLUETOOTH_SERVICE = 42;
private static final int MESSAGE_BLUETOOTH_STATE_CHANGE=60;
private static final int MESSAGE_TIMEOUT_BIND =100;
private static final int MESSAGE_TIMEOUT_UNBIND =101;
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 MAX_SAVE_RETRIES=3;
private final Context mContext;
// Locks are not provided for mName and mAddress.
// They are accessed in handler or broadcast receiver, same thread context.
private String mAddress;
private String mName;
private final ContentResolver mContentResolver;
private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks;
private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks;
private IBluetooth mBluetooth;
private boolean mBinding;
private boolean mUnbinding;
private boolean mQuietEnable = false;
private boolean mEnable;
private int mState;
private HandlerThread mThread;
private final BluetoothHandler mHandler;
private void registerForAirplaneMode(IntentFilter filter) {
final ContentResolver resolver = mContext.getContentResolver();
final String airplaneModeRadios = Settings.Global.getString(resolver,
Settings.Global.AIRPLANE_MODE_RADIOS);
final String toggleableRadios = Settings.Global.getString(resolver,
Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
boolean mIsAirplaneSensitive = airplaneModeRadios == null ? true :
airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH);
if (mIsAirplaneSensitive) {
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
}
}
private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() {
@Override
public void onBluetoothStateChange(int prevState, int newState) throws RemoteException {
Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE,prevState,newState);
mHandler.sendMessage(msg);
}
};
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) {
String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME);
if (DBG) Log.d(TAG, "Bluetooth Adapter name changed to " + newName);
if (newName != null) {
storeNameAndAddress(newName, null);
}
} else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
if (isAirplaneModeOn()) {
// disable without persisting the setting
mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE,
0, 0));
} else if (isBluetoothPersistedStateOn()) {
// enable without persisting the setting
mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE,
0, 0));
}
} else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_SWITCHED,
intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
}
}
};
BluetoothManagerService(Context context) {
mThread = new HandlerThread("BluetoothManager");
mThread.start();
mHandler = new BluetoothHandler(mThread.getLooper());
mContext = context;
mBluetooth = null;
mBinding = false;
mUnbinding = false;
mEnable = false;
mState = BluetoothAdapter.STATE_OFF;
mAddress = null;
mName = null;
mContentResolver = context.getContentResolver();
mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>();
mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>();
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
filter.addAction(Intent.ACTION_USER_SWITCHED);
registerForAirplaneMode(filter);
mContext.registerReceiver(mReceiver, filter);
boolean airplaneModeOn = isAirplaneModeOn();
boolean bluetoothOn = isBluetoothPersistedStateOn();
loadStoredNameAndAddress();
if (DBG) Log.d(TAG, "airplaneModeOn: " + airplaneModeOn + " bluetoothOn: " + bluetoothOn);
if (bluetoothOn) {
//Enable
if (DBG) Log.d(TAG, "Auto-enabling Bluetooth.");
enableHelper();
}
if (!isNameAndAddressSet()) {
//Sync the Bluetooth name and address from the Bluetooth Adapter
if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address...");
getNameAndAddress();
}
}
/**
* Returns true if airplane mode is currently on
*/
private final boolean isAirplaneModeOn() {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
}
/**
* Returns true if the Bluetooth saved state is "on"
*/
private final boolean isBluetoothPersistedStateOn() {
return Settings.Global.getInt(mContentResolver,
Settings.Global.BLUETOOTH_ON, 0) ==1;
}
/**
* Save the Bluetooth on/off state
*
*/
private void persistBluetoothSetting(boolean setOn) {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.BLUETOOTH_ON,
setOn ? 1 : 0);
}
/**
* Returns true if the Bluetooth Adapter's name and address is
* locally cached
* @return
*/
private boolean isNameAndAddressSet() {
return mName !=null && mAddress!= null && mName.length()>0 && mAddress.length()>0;
}
/**
* Retrieve the Bluetooth Adapter's name and address and save it in
* in the local cache
*/
private void loadStoredNameAndAddress() {
if (DBG) Log.d(TAG, "Loading stored name and address");
if (mContext.getResources().getBoolean
(com.android.internal.R.bool.config_bluetooth_address_validation) &&
Settings.Secure.getInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0) == 0) {
// if the valid flag is not set, don't load the address and name
if (DBG) Log.d(TAG, "invalid bluetooth name and address stored");
return;
}
mName = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME);
mAddress = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS);
if (DBG) Log.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress);
}
/**
* Save the Bluetooth name and address in the persistent store.
* Only non-null values will be saved.
* @param name
* @param address
*/
private void storeNameAndAddress(String name, String address) {
if (name != null) {
Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name);
mName = name;
if (DBG) Log.d(TAG,"Stored Bluetooth name: " +
Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_NAME));
}
if (address != null) {
Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address);
mAddress=address;
if (DBG) Log.d(TAG,"Stored Bluetoothaddress: " +
Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_ADDRESS));
}
if ((name != null) && (address != null)) {
Settings.Secure.putInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 1);
}
}
public IBluetooth registerAdapter(IBluetoothManagerCallback callback){
Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER);
msg.obj = callback;
mHandler.sendMessage(msg);
synchronized(mConnection) {
return mBluetooth;
}
}
public void unregisterAdapter(IBluetoothManagerCallback callback) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
"Need BLUETOOTH permission");
Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER);
msg.obj = callback;
mHandler.sendMessage(msg);
}
public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
"Need BLUETOOTH permission");
Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK);
msg.obj = callback;
mHandler.sendMessage(msg);
}
public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
"Need BLUETOOTH permission");
Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK);
msg.obj = callback;
mHandler.sendMessage(msg);
}
public boolean isEnabled() {
if (!checkIfCallerIsForegroundUser()) {
Log.w(TAG,"isEnabled(): not allowed for non-active user");
return false;
}
synchronized(mConnection) {
try {
return (mBluetooth != null && mBluetooth.isEnabled());
} catch (RemoteException e) {
Log.e(TAG, "isEnabled()", e);
}
}
return false;
}
public void getNameAndAddress() {
if (DBG) {
Log.d(TAG,"getNameAndAddress(): mBluetooth = " + mBluetooth +
" mBinding = " + mBinding);
}
Message msg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
mHandler.sendMessage(msg);
}
public boolean enableNoAutoConnect()
{
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
if (!checkIfCallerIsForegroundUser()) {
Log.w(TAG,"enableNoAutoConnect(): not allowed for non-active user");
return false;
}
if (DBG) {
Log.d(TAG,"enableNoAutoConnect(): mBluetooth =" + mBluetooth +
" mBinding = " + mBinding);
}
if (Binder.getCallingUid() != Process.NFC_UID) {
throw new SecurityException("no permission to enable Bluetooth quietly");
}
Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
msg.arg1=0; //No persist
msg.arg2=1; //Quiet mode
mHandler.sendMessage(msg);
return true;
}
public boolean enable() {
if (!checkIfCallerIsForegroundUser()) {
Log.w(TAG,"enable(): not allowed for non-active user");
return false;
}
return enableHelper();
}
public boolean disable(boolean persist) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permissicacheNameAndAddresson");
if (!checkIfCallerIsForegroundUser()) {
Log.w(TAG,"disable(): not allowed for non-active user");
return false;
}
if (DBG) {
Log.d(TAG,"disable(): mBluetooth = " + mBluetooth +
" mBinding = " + mBinding);
}
Message msg = mHandler.obtainMessage(MESSAGE_DISABLE);
msg.arg1=(persist?1:0);
mHandler.sendMessage(msg);
return true;
}
public void unbindAndFinish() {
if (DBG) {
Log.d(TAG,"unbindAndFinish(): " + mBluetooth +
" mBinding = " + mBinding);
}
synchronized (mConnection) {
if (mUnbinding) return;
mUnbinding = true;
if (mBluetooth != null) {
if (!mConnection.isGetNameAddressOnly()) {
//Unregister callback object
try {
mBluetooth.unregisterCallback(mBluetoothCallback);
} catch (RemoteException re) {
Log.e(TAG, "Unable to unregister BluetoothCallback",re);
}
}
if (DBG) Log.d(TAG, "Sending unbind request.");
mBluetooth = null;
//Unbind
mContext.unbindService(mConnection);
mUnbinding = false;
mBinding = false;
} else {
mUnbinding=false;
}
}
}
private void sendBluetoothStateCallback(boolean isUp) {
int n = mStateChangeCallbacks.beginBroadcast();
if (DBG) Log.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers.");
for (int i=0; i <n;i++) {
try {
mStateChangeCallbacks.getBroadcastItem(i).onBluetoothStateChange(isUp);
} catch (RemoteException e) {
Log.e(TAG, "Unable to call onBluetoothStateChange() on callback #" + i , e);
}
}
mStateChangeCallbacks.finishBroadcast();
}
/**
* Inform BluetoothAdapter instances that Adapter service is up
*/
private void sendBluetoothServiceUpCallback() {
if (!mConnection.isGetNameAddressOnly()) {
if (DBG) Log.d(TAG,"Calling onBluetoothServiceUp callbacks");
int n = mCallbacks.beginBroadcast();
Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
for (int i=0; i <n;i++) {
try {
mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
} catch (RemoteException e) {
Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
}
}
mCallbacks.finishBroadcast();
}
}
/**
* Inform BluetoothAdapter instances that Adapter service is down
*/
private void sendBluetoothServiceDownCallback() {
if (!mConnection.isGetNameAddressOnly()) {
if (DBG) Log.d(TAG,"Calling onBluetoothServiceDown callbacks");
int n = mCallbacks.beginBroadcast();
Log.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
for (int i=0; i <n;i++) {
try {
mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
} catch (RemoteException e) {
Log.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
}
}
mCallbacks.finishBroadcast();
}
}
public String getAddress() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
if (!checkIfCallerIsForegroundUser()) {
Log.w(TAG,"getAddress(): not allowed for non-active user");
return mAddress;
}
synchronized(mConnection) {
if (mBluetooth != null) {
try {
return mBluetooth.getAddress();
} catch (RemoteException e) {
Log.e(TAG, "getAddress(): Unable to retrieve address remotely..Returning cached address",e);
}
}
}
// mAddress is accessed from outside.
// It is alright without a lock. Here, bluetooth is off, no other thread is
// changing mAddress
return mAddress;
}
public String getName() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
if (!checkIfCallerIsForegroundUser()) {
Log.w(TAG,"getName(): not allowed for non-active user");
return mName;
}
synchronized(mConnection) {
if (mBluetooth != null) {
try {
return mBluetooth.getName();
} catch (RemoteException e) {
Log.e(TAG, "getName(): Unable to retrieve name remotely..Returning cached name",e);
}
}
}
// mName is accessed from outside.
// It alright without a lock. Here, bluetooth is off, no other thread is
// changing mName
return mName;
}
private class BluetoothServiceConnection implements ServiceConnection {
private boolean mGetNameAddressOnly;
public void setGetNameAddressOnly(boolean getOnly) {
mGetNameAddressOnly = getOnly;
}
public boolean isGetNameAddressOnly() {
return mGetNameAddressOnly;
}
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "BluetoothServiceConnection: connected to AdapterService");
Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
msg.obj = service;
mHandler.sendMessage(msg);
}
public void onServiceDisconnected(ComponentName className) {
// Called if we unexpected disconnected.
if (DBG) Log.d(TAG, "BluetoothServiceConnection: disconnected from AdapterService");
Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
mHandler.sendMessage(msg);
}
}
private BluetoothServiceConnection mConnection = new BluetoothServiceConnection();
private class BluetoothHandler extends Handler {
public BluetoothHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
if (DBG) Log.d (TAG, "Message: " + msg.what);
switch (msg.what) {
case MESSAGE_GET_NAME_AND_ADDRESS: {
if (DBG) Log.d(TAG,"MESSAGE_GET_NAME_AND_ADDRESS");
synchronized(mConnection) {
//Start bind request
if ((mBluetooth == null) && (!mBinding)) {
if (DBG) Log.d(TAG, "Binding to service to get name and address");
mConnection.setGetNameAddressOnly(true);
//Start bind timeout and bind
Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
Intent i = new Intent(IBluetooth.class.getName());
if (!mContext.bindService(i, mConnection,
Context.BIND_AUTO_CREATE, UserHandle.USER_CURRENT)) {
mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
Log.e(TAG, "fail to bind to: " + IBluetooth.class.getName());
} else {
mBinding = true;
}
}
else {
Message saveMsg= mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
saveMsg.arg1 = 0;
if (mBluetooth != null) {
mHandler.sendMessage(saveMsg);
} else {
// if enable is also called to bind the service
// wait for MESSAGE_BLUETOOTH_SERVICE_CONNECTED
mHandler.sendMessageDelayed(saveMsg, TIMEOUT_SAVE_MS);
}
}
}
break;
}
case MESSAGE_SAVE_NAME_AND_ADDRESS: {
boolean unbind = false;
if (DBG) Log.d(TAG,"MESSAGE_SAVE_NAME_AND_ADDRESS");
synchronized(mConnection) {
if (!mEnable && mBluetooth != null) {
try {
mBluetooth.enable();
} catch (RemoteException e) {
Log.e(TAG,"Unable to call enable()",e);
}
}
}
if (mBluetooth != null) waitForOnOff(true, false);
synchronized(mConnection) {
if (mBluetooth != null) {
String name = null;
String address = null;
try {
name = mBluetooth.getName();
address = mBluetooth.getAddress();
} catch (RemoteException re) {
Log.e(TAG,"",re);
}
if (name != null && address != null) {
storeNameAndAddress(name,address);
if (mConnection.isGetNameAddressOnly()) {
unbind = true;
}
} else {
if (msg.arg1 < MAX_SAVE_RETRIES) {
Message retryMsg = mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
retryMsg.arg1= 1+msg.arg1;
if (DBG) Log.d(TAG,"Retrying name/address remote retrieval and save.....Retry count =" + retryMsg.arg1);
mHandler.sendMessageDelayed(retryMsg, TIMEOUT_SAVE_MS);
} else {
Log.w(TAG,"Maximum name/address remote retrieval retry exceeded");
if (mConnection.isGetNameAddressOnly()) {
unbind = true;
}
}
}
if (!mEnable) {
try {
mBluetooth.disable();
} catch (RemoteException e) {
Log.e(TAG,"Unable to call disable()",e);
}
}
} else {
// rebind service by Request GET NAME AND ADDRESS
// if service is unbinded by disable or
// MESSAGE_BLUETOOTH_SERVICE_CONNECTED is not received
Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
mHandler.sendMessage(getMsg);
}
}
if (!mEnable && mBluetooth != null) waitForOnOff(false, true);
if (unbind) {
unbindAndFinish();
}
break;
}
case MESSAGE_ENABLE:
if (DBG) {
Log.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth);
}
mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
mEnable = true;
handleEnable(msg.arg1 == 1, msg.arg2 ==1);
break;
case MESSAGE_DISABLE:
mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
if (mEnable && mBluetooth != null) {
waitForOnOff(true, false);
mEnable = false;
handleDisable(msg.arg1 == 1);
waitForOnOff(false, false);
} else {
mEnable = false;
handleDisable(msg.arg1 == 1);
}
break;
case MESSAGE_REGISTER_ADAPTER:
{
IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
boolean added = mCallbacks.register(callback);
Log.d(TAG,"Added callback: " + (callback == null? "null": callback) +":" +added );
}
break;
case MESSAGE_UNREGISTER_ADAPTER:
{
IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
boolean removed = mCallbacks.unregister(callback);
Log.d(TAG,"Removed callback: " + (callback == null? "null": callback) +":" + removed);
break;
}
case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK:
{
IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
mStateChangeCallbacks.register(callback);
break;
}
case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK:
{
IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
mStateChangeCallbacks.unregister(callback);
break;
}
case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
{
if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED");
//Remove timeout
mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
IBinder service = (IBinder) msg.obj;
synchronized(mConnection) {
mBinding = false;
mBluetooth = IBluetooth.Stub.asInterface(service);
if (mConnection.isGetNameAddressOnly()) {
//Request GET NAME AND ADDRESS
Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
mHandler.sendMessage(getMsg);
if (!mEnable) return;
}
mConnection.setGetNameAddressOnly(false);
//Register callback object
try {
mBluetooth.registerCallback(mBluetoothCallback);
} catch (RemoteException re) {
Log.e(TAG, "Unable to register BluetoothCallback",re);
}
//Inform BluetoothAdapter instances that service is up
sendBluetoothServiceUpCallback();
//Do enable request
try {
if (mQuietEnable == false) {
if(!mBluetooth.enable()) {
Log.e(TAG,"IBluetooth.enable() returned false");
}
}
else
{
if(!mBluetooth.enableNoAutoConnect()) {
Log.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
}
}
} catch (RemoteException e) {
Log.e(TAG,"Unable to call enable()",e);
}
}
if (!mEnable) {
waitForOnOff(true, false);
handleDisable(false);
waitForOnOff(false, false);
}
break;
}
case MESSAGE_TIMEOUT_BIND: {
Log.e(TAG, "MESSAGE_TIMEOUT_BIND");
synchronized(mConnection) {
mBinding = false;
}
break;
}
case MESSAGE_BLUETOOTH_STATE_CHANGE:
{
int prevState = msg.arg1;
int newState = msg.arg2;
if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: prevState = " + prevState + ", newState=" + newState);
mState = newState;
bluetoothStateChangeHandler(prevState, newState);
break;
}
case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
{
Log.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED");
synchronized(mConnection) {
// if service is unbinded already, do nothing and return
if (mBluetooth == null) return;
mBluetooth = null;
}
if (mEnable) {
mEnable = false;
// Send a Bluetooth Restart message
Message restartMsg = mHandler.obtainMessage(
MESSAGE_RESTART_BLUETOOTH_SERVICE);
mHandler.sendMessageDelayed(restartMsg,
SERVICE_RESTART_TIME_MS);
}
if (!mConnection.isGetNameAddressOnly()) {
sendBluetoothServiceDownCallback();
// Send BT state broadcast to update
// the BT icon correctly
bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
BluetoothAdapter.STATE_TURNING_OFF);
mState = BluetoothAdapter.STATE_OFF;
}
break;
}
case MESSAGE_RESTART_BLUETOOTH_SERVICE:
{
Log.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE:"
+" Restart IBluetooth service");
/* Enable without persisting the setting as
it doesnt change when IBluetooth
service restarts */
mEnable = true;
handleEnable(false, mQuietEnable);
break;
}
case MESSAGE_TIMEOUT_UNBIND:
{
Log.e(TAG, "MESSAGE_TIMEOUT_UNBIND");
synchronized(mConnection) {
mUnbinding = false;
}
break;
}
case MESSAGE_USER_SWITCHED:
{
if (DBG) {
Log.d(TAG, "MESSAGE_USER_SWITCHED");
}
mHandler.removeMessages(MESSAGE_USER_SWITCHED);
/* disable and enable BT when detect a user switch */
if (mEnable && mBluetooth != null) {
synchronized (mConnection) {
if (mBluetooth != null) {
//Unregister callback object
try {
mBluetooth.unregisterCallback(mBluetoothCallback);
} catch (RemoteException re) {
Log.e(TAG, "Unable to unregister",re);
}
}
}
mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
waitForOnOff(true, false);
bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON);
// disable
handleDisable(false);
waitForOnOff(false, true);
bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
BluetoothAdapter.STATE_OFF);
mState = BluetoothAdapter.STATE_OFF;
sendBluetoothServiceDownCallback();
synchronized (mConnection) {
if (mBluetooth != null) {
mBluetooth = null;
//Unbind
mContext.unbindService(mConnection);
}
}
SystemClock.sleep(100);
// enable
handleEnable(false, mQuietEnable);
} else if (mBinding || mBluetooth != null) {
Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED);
userMsg.arg2 = 1 + msg.arg2;
// if user is switched when service is being binding
// delay sending MESSAGE_USER_SWITCHED
mHandler.sendMessageDelayed(userMsg, USER_SWITCHED_TIME_MS);
if (DBG) {
Log.d(TAG, "delay MESSAGE_USER_SWITCHED " + userMsg.arg2);
}
}
break;
}
}
}
}
private void handleEnable(boolean persist, boolean quietMode) {
if (persist) {
persistBluetoothSetting(true);
}
mQuietEnable = quietMode;
synchronized(mConnection) {
if ((mBluetooth == null) && (!mBinding)) {
//Start bind timeout and bind
Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
mConnection.setGetNameAddressOnly(false);
Intent i = new Intent(IBluetooth.class.getName());
if (!mContext.bindService(i, mConnection,Context.BIND_AUTO_CREATE,
UserHandle.USER_CURRENT)) {
mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
Log.e(TAG, "Fail to bind to: " + IBluetooth.class.getName());
} else {
mBinding = true;
}
} else if (mBluetooth != null) {
if (mConnection.isGetNameAddressOnly()) {
// if GetNameAddressOnly is set, we can clear this flag,
// so the service won't be unbind
// after name and address are saved
mConnection.setGetNameAddressOnly(false);
//Register callback object
try {
mBluetooth.registerCallback(mBluetoothCallback);
} catch (RemoteException re) {
Log.e(TAG, "Unable to register BluetoothCallback",re);
}
//Inform BluetoothAdapter instances that service is up
sendBluetoothServiceUpCallback();
}
//Enable bluetooth
try {
if (!mQuietEnable) {
if(!mBluetooth.enable()) {
Log.e(TAG,"IBluetooth.enable() returned false");
}
}
else {
if(!mBluetooth.enableNoAutoConnect()) {
Log.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
}
}
} catch (RemoteException e) {
Log.e(TAG,"Unable to call enable()",e);
}
}
}
}
private void handleDisable(boolean persist) {
if (persist) {
persistBluetoothSetting(false);
}
synchronized(mConnection) {
// don't need to disable if GetNameAddressOnly is set,
// service will be unbinded after Name and Address are saved
if ((mBluetooth != null) && (!mConnection.isGetNameAddressOnly())) {
if (DBG) Log.d(TAG,"Sending off request.");
try {
if(!mBluetooth.disable()) {
Log.e(TAG,"IBluetooth.disable() returned false");
}
} catch (RemoteException e) {
Log.e(TAG,"Unable to call disable()",e);
}
}
}
}
private boolean checkIfCallerIsForegroundUser() {
int foregroundUser;
int callingUser = UserHandle.getCallingUserId();
long callingIdentity = Binder.clearCallingIdentity();
boolean valid = false;
try {
foregroundUser = ActivityManager.getCurrentUser();
valid = (callingUser == foregroundUser);
if (DBG) {
Log.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid
+ " callingUser=" + callingUser
+ " foregroundUser=" + foregroundUser);
}
} finally {
Binder.restoreCallingIdentity(callingIdentity);
}
return valid;
}
private boolean enableHelper() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
if (DBG) {
Log.d(TAG,"enable(): mBluetooth =" + mBluetooth +
" mBinding = " + mBinding);
}
Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
msg.arg1=1; //persist
msg.arg2=0; //No Quiet Mode
mHandler.sendMessage(msg);
return true;
}
private void bluetoothStateChangeHandler(int prevState, int newState) {
if (prevState != newState) {
//Notify all proxy objects first of adapter state change
if (newState == BluetoothAdapter.STATE_ON || newState == BluetoothAdapter.STATE_OFF) {
boolean isUp = (newState==BluetoothAdapter.STATE_ON);
sendBluetoothStateCallback(isUp);
//If Bluetooth is off, send service down event to proxy objects, and unbind
if (!isUp) {
//Only unbind with mEnable flag not set
//For race condition: disable and enable back-to-back
//Avoid unbind right after enable due to callback from disable
if ((!mEnable) && (mBluetooth != null)) {
sendBluetoothServiceDownCallback();
unbindAndFinish();
}
}
}
//Send broadcast message to everyone else
Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
BLUETOOTH_PERM);
}
}
/**
* if on is true, wait for state become ON
* if off is true, wait for state become OFF
* if both on and off are false, wait for state not ON
*/
private boolean waitForOnOff(boolean on, boolean off) {
int i = 0;
while (i < 10) {
synchronized(mConnection) {
try {
if (mBluetooth == null) break;
if (on) {
if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true;
} else if (off) {
if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true;
} else {
if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true;
}
} catch (RemoteException e) {
Log.e(TAG, "getState()", e);
break;
}
}
if (on || off) {
SystemClock.sleep(300);
} else {
SystemClock.sleep(50);
}
i++;
}
Log.e(TAG,"waitForOnOff time out");
return false;
}
}