Add ability to turn BT on / off on a per application basis.
This changes adds an API for system applications
to enable bluetooth without all the side effects like
auto connection to headsets etc.
Also some tweaks to the adapter state machine
Change-Id: Ib9f22d548a26d72334b300101c8eb0d80f08a4bb
diff --git a/Android.mk b/Android.mk
index 7b8d0bc..737202d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -93,6 +93,7 @@
core/java/android/bluetooth/IBluetoothHeadset.aidl \
core/java/android/bluetooth/IBluetoothHealthCallback.aidl \
core/java/android/bluetooth/IBluetoothPbap.aidl \
+ core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl \
core/java/android/content/IClipboard.aidl \
core/java/android/content/IContentService.aidl \
core/java/android/content/IIntentReceiver.aidl \
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index b993bd8..ca6f085 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1143,6 +1143,69 @@
}
}
+ /**
+ * Enable control of the Bluetooth Adapter for a single application.
+ *
+ * <p>Some applications need to use Bluetooth for short periods of time to
+ * transfer data but don't want all the associated implications like
+ * automatic connection to headsets etc.
+ *
+ * <p> Multiple applications can call this. This is reference counted and
+ * Bluetooth disabled only when no one else is using it. There will be no UI
+ * shown to the user while bluetooth is being enabled. Any user action will
+ * override this call. For example, if user wants Bluetooth on and the last
+ * user of this API wanted to disable Bluetooth, Bluetooth will not be
+ * turned off.
+ *
+ * <p> This API is only meant to be used by internal applications. Third
+ * party applications but use {@link #enable} and {@link #disable} APIs.
+ *
+ * <p> If this API returns true, it means the callback will be called.
+ * The callback will be called with the current state of Bluetooth.
+ * If the state is not what was requested, an internal error would be the
+ * reason.
+ *
+ * @param on True for on, false for off.
+ * @param callback The callback to notify changes to the state.
+ * @hide
+ */
+ public boolean changeApplicationBluetoothState(boolean on,
+ BluetoothStateChangeCallback callback) {
+ if (callback == null) return false;
+
+ try {
+ return mService.changeApplicationBluetoothState(on, new
+ StateChangeCallbackWrapper(callback), new Binder());
+ } catch (RemoteException e) {
+ Log.e(TAG, "changeBluetoothState", e);
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public interface BluetoothStateChangeCallback {
+ public void onBluetoothStateChange(boolean on);
+ }
+
+ /**
+ * @hide
+ */
+ public class StateChangeCallbackWrapper extends IBluetoothStateChangeCallback.Stub {
+ private BluetoothStateChangeCallback mCallback;
+
+ StateChangeCallbackWrapper(BluetoothStateChangeCallback
+ callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onBluetoothStateChange(boolean on) {
+ mCallback.onBluetoothStateChange(on);
+ }
+ }
+
private Set<BluetoothDevice> toDeviceSet(String[] addresses) {
Set<BluetoothDevice> devices = new HashSet<BluetoothDevice>(addresses.length);
for (int i = 0; i < addresses.length; i++) {
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 183772d..be43c51 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -17,6 +17,7 @@
package android.bluetooth;
import android.bluetooth.IBluetoothCallback;
+import android.bluetooth.IBluetoothStateChangeCallback;
import android.bluetooth.IBluetoothHealthCallback;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHealthAppConfiguration;
@@ -52,6 +53,9 @@
byte[] readOutOfBandData();
int getAdapterConnectionState();
+ boolean changeApplicationBluetoothState(boolean on,
+ in IBluetoothStateChangeCallback callback, in
+ IBinder b);
boolean createBond(in String address);
boolean createBondOutOfBand(in String address, in byte[] hash, in byte[] randomizer);
diff --git a/core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl b/core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl
new file mode 100644
index 0000000..feccdce
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011, 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;
+
+/**
+ * System private API for Bluetooth state change callback.
+ *
+ * {@hide}
+ */
+interface IBluetoothStateChangeCallback
+{
+ void onBluetoothStateChange(boolean on);
+}
diff --git a/core/java/android/server/BluetoothAdapterStateMachine.java b/core/java/android/server/BluetoothAdapterStateMachine.java
index ae91465..83f5a9f 100644
--- a/core/java/android/server/BluetoothAdapterStateMachine.java
+++ b/core/java/android/server/BluetoothAdapterStateMachine.java
@@ -17,11 +17,13 @@
package android.server;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.IBluetoothStateChangeCallback;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Message;
+import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log;
@@ -34,17 +36,18 @@
/**
* Bluetooth Adapter StateMachine
* All the states are at the same level, ie, no hierarchy.
- * (BluetootOn)
- * | ^
- * TURN_OFF | | BECOME_PAIRABLE
- * AIRPLANE_MODE_ON | |
- * V |
- * (Switching)
- * | ^
- * BECOME_NON_PAIRABLE& | | TURN_ON(_CONTINUE)/TURN_ON_FOR_PRIVILEGED
- * ALL_DEVICES_DISCONNECTED | |
- * V |
- * (HotOff)
+ * (BluetootOn)<----------------------<-
+ * | ^ -------------------->- |
+ * | | | |
+ * TURN_OFF | | BECAME_PAIRABLE m1 | | USER_TURN_ON
+ * AIRPLANE_MODE_ON | | | |
+ * V | | |
+ * (Switching) (PerProcessState)
+ * | ^ | |
+ * BECAME_NON_PAIRABLE& | | TURN_ON(_CONTINUE) | |
+ * ALL_DEVICES_DISCONNECTED | | m2 | |
+ * V |------------------------< | BECAME_PAIRABLE
+ * (HotOff)---------------------------- PER_PROCESS_TURN_ON
* / ^
* / | SERVICE_RECORD_LOADED
* | |
@@ -55,6 +58,10 @@
* V |
* (PowerOff) <----- initial state
*
+ * Legend:
+ * m1 = USER_TURN_OFF
+ * m2 = Transition to HotOff when number of process wanting BT on is 0.
+ * BECAME_NON_PAIRABLE will make the transition.
*/
final class BluetoothAdapterStateMachine extends StateMachine {
private static final String TAG = "BluetoothAdapterStateMachine";
@@ -63,24 +70,24 @@
// Message(what) to take an action
//
// We get this message when user tries to turn on BT
- public static final int USER_TURN_ON = 1;
+ static final int USER_TURN_ON = 1;
// We get this message when user tries to turn off BT
- public static final int USER_TURN_OFF = 2;
+ static final int USER_TURN_OFF = 2;
// Message(what) to report a event that the state machine need to respond to
//
// Event indicates sevice records have been loaded
- public static final int SERVICE_RECORD_LOADED = 51;
+ static final int SERVICE_RECORD_LOADED = 51;
// Event indicates all the remote Bluetooth devices has been disconnected
- public static final int ALL_DEVICES_DISCONNECTED = 52;
+ static final int ALL_DEVICES_DISCONNECTED = 52;
// Event indicates the Bluetooth is connectable
- public static final int BECOME_PAIRABLE = 53;
+ static final int BECAME_PAIRABLE = 53;
// Event indicates the Bluetooth is non-connectable.
- public static final int BECOME_NON_PAIRABLE = 54;
+ static final int BECAME_NON_PAIRABLE = 54;
// Event indicates airplane mode is turned on
- public static final int AIRPLANE_MODE_ON = 55;
+ static final int AIRPLANE_MODE_ON = 55;
// Event indicates airplane mode is turned off
- public static final int AIRPLANE_MODE_OFF = 56;
+ static final int AIRPLANE_MODE_OFF = 56;
// private internal messages
//
@@ -95,8 +102,9 @@
private static final int TURN_ON_CONTINUE = 102;
// Unload firmware, turning off Bluetooth module power
private static final int TURN_COLD = 103;
- // For NFC, turn on bluetooth for certain process
- private static final int TURN_ON_FOR_PRIVILEGED = 104;
+ // Per process enable / disable messages
+ static final int PER_PROCESS_TURN_ON = 104;
+ static final int PER_PROCESS_TURN_OFF = 105;
private Context mContext;
private BluetoothService mBluetoothService;
@@ -107,6 +115,7 @@
private HotOff mHotOff;
private WarmUp mWarmUp;
private PowerOff mPowerOff;
+ private PerProcessState mPerProcessState;
// this is the BluetoothAdapter state that reported externally
private int mPublicState;
@@ -123,12 +132,15 @@
mHotOff = new HotOff();
mWarmUp = new WarmUp();
mPowerOff = new PowerOff();
+ mPerProcessState = new PerProcessState();
addState(mBluetoothOn);
addState(mSwitching);
addState(mHotOff);
addState(mWarmUp);
addState(mPowerOff);
+ addState(mPerProcessState);
+
setInitialState(mPowerOff);
mPublicState = BluetoothAdapter.STATE_OFF;
@@ -142,12 +154,9 @@
* Bluetooth module's power is off, firmware is not loaded.
*/
private class PowerOff extends State {
- private boolean mPersistSwitchOn = false;
-
@Override
public void enter() {
- if (DBG) log("Enter PowerOff: " + mPersistSwitchOn);
- mPersistSwitchOn = false;
+ if (DBG) log("Enter PowerOff: ");
}
@Override
public boolean processMessage(Message message) {
@@ -162,7 +171,7 @@
if (prepareBluetooth()) {
// this is user request, save the setting
if ((Boolean) message.obj) {
- mPersistSwitchOn = true;
+ persistSwitchSetting(true);
}
// We will continue turn the BT on all the way to the BluetoothOn state
deferMessage(obtainMessage(TURN_ON_CONTINUE));
@@ -191,9 +200,21 @@
transitionTo(mPowerOff);
broadcastState(BluetoothAdapter.STATE_OFF);
}
+ } else if (mContext.getResources().getBoolean
+ (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {
+ sendMessage(TURN_HOT);
}
break;
- case AIRPLANE_MODE_ON: // ignore
+ case PER_PROCESS_TURN_ON:
+ if (prepareBluetooth()) {
+ transitionTo(mWarmUp);
+ }
+ deferMessage(obtainMessage(PER_PROCESS_TURN_ON));
+ break;
+ case PER_PROCESS_TURN_OFF:
+ perProcessCallback(false, (IBluetoothStateChangeCallback) message.obj);
+ break;
+ case AIRPLANE_MODE_ON:
case USER_TURN_OFF: // ignore
break;
default:
@@ -276,6 +297,8 @@
// on to the BluetoothOn state
case AIRPLANE_MODE_ON:
case AIRPLANE_MODE_OFF:
+ case PER_PROCESS_TURN_ON:
+ case PER_PROCESS_TURN_OFF:
deferMessage(message);
break;
case USER_TURN_OFF: // ignore
@@ -294,12 +317,9 @@
* non-connectable.
*/
private class HotOff extends State {
- private boolean mPersistSwitchOn = false;
-
@Override
public void enter() {
- if (DBG) log("Enter HotOff: " + mPersistSwitchOn);
- mPersistSwitchOn = false;
+ if (DBG) log("Enter HotOff:");
}
@Override
@@ -310,9 +330,10 @@
switch(message.what) {
case USER_TURN_ON:
if ((Boolean) message.obj) {
- mPersistSwitchOn = true;
+ persistSwitchSetting(true);
}
// let it fall to TURN_ON_CONTINUE:
+ //$FALL-THROUGH$
case TURN_ON_CONTINUE:
mBluetoothService.switchConnectable(true);
transitionTo(mSwitching);
@@ -328,13 +349,25 @@
break;
case AIRPLANE_MODE_OFF:
if (getBluetoothPersistedSetting()) {
- mBluetoothService.switchConnectable(true);
transitionTo(mSwitching);
+ mBluetoothService.switchConnectable(true);
broadcastState(BluetoothAdapter.STATE_TURNING_ON);
}
break;
case USER_TURN_OFF: // ignore
break;
+ case PER_PROCESS_TURN_ON:
+ transitionTo(mPerProcessState);
+
+ // Resend the PER_PROCESS_TURN_ON message so that the callback
+ // can be sent through.
+ deferMessage(message);
+
+ mBluetoothService.switchConnectable(true);
+ break;
+ case PER_PROCESS_TURN_OFF:
+ perProcessCallback(false, (IBluetoothStateChangeCallback)message.obj);
+ break;
default:
return NOT_HANDLED;
}
@@ -356,11 +389,7 @@
boolean retValue = HANDLED;
switch(message.what) {
- case BECOME_PAIRABLE:
- if (mPowerOff.mPersistSwitchOn || mHotOff.mPersistSwitchOn) {
- persistSwitchSetting(true);
- mPowerOff.mPersistSwitchOn = mHotOff.mPersistSwitchOn = false;
- }
+ case BECAME_PAIRABLE:
String[] propVal = {"Pairable", mBluetoothService.getProperty("Pairable")};
mEventLoop.onPropertyChanged(propVal);
@@ -369,7 +398,7 @@
transitionTo(mBluetoothOn);
broadcastState(BluetoothAdapter.STATE_ON);
break;
- case BECOME_NON_PAIRABLE:
+ case BECAME_NON_PAIRABLE:
if (mBluetoothService.getAdapterConnectionState() ==
BluetoothAdapter.STATE_DISCONNECTED) {
transitionTo(mHotOff);
@@ -385,9 +414,12 @@
case USER_TURN_ON:
case AIRPLANE_MODE_OFF:
case AIRPLANE_MODE_ON:
+ case PER_PROCESS_TURN_ON:
+ case PER_PROCESS_TURN_OFF:
case USER_TURN_OFF:
deferMessage(message);
break;
+
default:
return NOT_HANDLED;
}
@@ -395,10 +427,6 @@
}
private void finishSwitchingOff() {
- if (mBluetoothOn.mPersistBluetoothOff) {
- persistSwitchSetting(false);
- mBluetoothOn.mPersistBluetoothOff = false;
- }
mBluetoothService.finishDisable();
if (mContext.getResources().getBoolean
(com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {
@@ -415,7 +443,6 @@
@Override
public void enter() {
if (DBG) log("Enter BluetoothOn: " + mPersistBluetoothOff);
- mPersistBluetoothOff = false;
}
@Override
public boolean processMessage(Message message) {
@@ -425,22 +452,40 @@
switch(message.what) {
case USER_TURN_OFF:
if ((Boolean) message.obj) {
- mPersistBluetoothOff = true;
+ persistSwitchSetting(false);
}
- // let it fall through to AIRPLANE_MODE_ON
+
+ if (mBluetoothService.isDiscovering()) {
+ mBluetoothService.cancelDiscovery();
+ }
+ if (!mBluetoothService.isApplicationStateChangeTrackerEmpty()) {
+ transitionTo(mPerProcessState);
+ deferMessage(obtainMessage(USER_TURN_OFF));
+ break;
+ }
+ //$FALL-THROUGH$ to AIRPLANE_MODE_ON
case AIRPLANE_MODE_ON:
transitionTo(mSwitching);
broadcastState(BluetoothAdapter.STATE_TURNING_OFF);
mBluetoothService.switchConnectable(false);
mBluetoothService.disconnectDevices();
+
// we turn all the way to PowerOff with AIRPLANE_MODE_ON
if (message.what == AIRPLANE_MODE_ON) {
+ // We inform all the per process callbacks
+ allProcessesCallback(false);
deferMessage(obtainMessage(AIRPLANE_MODE_ON));
}
break;
case AIRPLANE_MODE_OFF: // ignore
case USER_TURN_ON: // ignore
break;
+ case PER_PROCESS_TURN_ON:
+ perProcessCallback(true, (IBluetoothStateChangeCallback)message.obj);
+ break;
+ case PER_PROCESS_TURN_OFF:
+ perProcessCallback(false, (IBluetoothStateChangeCallback)message.obj);
+ break;
default:
return NOT_HANDLED;
}
@@ -449,6 +494,101 @@
}
+
+ private class PerProcessState extends State {
+ IBluetoothStateChangeCallback mCallback = null;
+
+ @Override
+ public void enter() {
+ if (DBG) log("Enter PerProcessState");
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) log("PerProcessState process message: " + message.what);
+
+ boolean retValue = HANDLED;
+ switch (message.what) {
+ case PER_PROCESS_TURN_ON:
+ mCallback = (IBluetoothStateChangeCallback)getCurrentMessage().obj;
+
+ // If this is not the first application call the callback.
+ if (mBluetoothService.getNumberOfApplicationStateChangeTrackers() > 1) {
+ perProcessCallback(true, mCallback);
+ }
+ break;
+ case BECAME_PAIRABLE:
+ perProcessCallback(true, mCallback);
+ break;
+ case USER_TURN_ON:
+ broadcastState(BluetoothAdapter.STATE_TURNING_ON);
+ persistSwitchSetting(true);
+
+ String[] propVal = {"Pairable", mBluetoothService.getProperty("Pairable")};
+ mEventLoop.onPropertyChanged(propVal);
+
+ // run bluetooth now that it's turned on
+ mBluetoothService.runBluetooth();
+ transitionTo(mBluetoothOn);
+ broadcastState(BluetoothAdapter.STATE_ON);
+ break;
+ case USER_TURN_OFF:
+ broadcastState(BluetoothAdapter.STATE_TURNING_OFF);
+ if (mBluetoothService.getAdapterConnectionState() !=
+ BluetoothAdapter.STATE_DISCONNECTED) {
+ mBluetoothService.disconnectDevices();
+ break;
+ }
+ //$FALL-THROUGH$ all devices are already disconnected
+ case ALL_DEVICES_DISCONNECTED:
+ mBluetoothService.finishDisable();
+ broadcastState(BluetoothAdapter.STATE_OFF);
+ break;
+ case PER_PROCESS_TURN_OFF:
+ perProcessCallback(false, (IBluetoothStateChangeCallback)message.obj);
+ if (mBluetoothService.isApplicationStateChangeTrackerEmpty()) {
+ mBluetoothService.switchConnectable(false);
+ }
+ break;
+ case BECAME_NON_PAIRABLE:
+ transitionTo(mHotOff);
+ if (!mContext.getResources().getBoolean
+ (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {
+ deferMessage(obtainMessage(TURN_COLD));
+ }
+ break;
+ case AIRPLANE_MODE_ON:
+ mBluetoothService.switchConnectable(false);
+ allProcessesCallback(false);
+ // we turn all the way to PowerOff with AIRPLANE_MODE_ON
+ deferMessage(obtainMessage(AIRPLANE_MODE_ON));
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return retValue;
+ }
+ }
+
+
+ private void perProcessCallback(boolean on, IBluetoothStateChangeCallback c) {
+ if (c == null) return;
+
+ try {
+ c.onBluetoothStateChange(on);
+ } catch (RemoteException e) {}
+ }
+
+ private void allProcessesCallback(boolean on) {
+ for (IBluetoothStateChangeCallback c:
+ mBluetoothService.getApplicationStateChangeCallbacks()) {
+ perProcessCallback(on, c);
+ }
+ if (!on) {
+ mBluetoothService.clearApplicationStateChangeTracker();
+ }
+ }
+
/**
* Return the public BluetoothAdapter state
*/
@@ -476,7 +616,7 @@
private void broadcastState(int newState) {
- if (DBG) log("Bluetooth state " + mPublicState + " -> " + newState);
+ log("Bluetooth state " + mPublicState + " -> " + newState);
if (mPublicState == newState) {
return;
}
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 107a2a9..2cab05c 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -340,9 +340,9 @@
if (name.equals("Pairable")) {
if (pairable.equals("true")) {
- mBluetoothState.sendMessage(BluetoothAdapterStateMachine.BECOME_PAIRABLE);
+ mBluetoothState.sendMessage(BluetoothAdapterStateMachine.BECAME_PAIRABLE);
} else {
- mBluetoothState.sendMessage(BluetoothAdapterStateMachine.BECOME_NON_PAIRABLE);
+ mBluetoothState.sendMessage(BluetoothAdapterStateMachine.BECAME_NON_PAIRABLE);
}
}
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 34f1971..9e66957 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -39,6 +39,7 @@
import android.bluetooth.IBluetooth;
import android.bluetooth.IBluetoothCallback;
import android.bluetooth.IBluetoothHealthCallback;
+import android.bluetooth.IBluetoothStateChangeCallback;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -68,13 +69,15 @@
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
-import java.io.InputStreamReader;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -101,6 +104,8 @@
private final BluetoothBondState mBondState; // local cache of bondings
private final IBatteryStats mBatteryStats;
private final Context mContext;
+ private Map<Integer, IBluetoothStateChangeCallback> mStateChangeTracker =
+ Collections.synchronizedMap(new HashMap<Integer, IBluetoothStateChangeCallback>());
private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
@@ -114,6 +119,9 @@
private static final int MESSAGE_UUID_INTENT = 1;
private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 2;
+ private static final int RFCOMM_RECORD_REAPER = 10;
+ private static final int STATE_CHANGE_REAPER = 11;
+
// The time (in millisecs) to delay the pairing attempt after the first
// auto pairing attempt fails. We use an exponential delay with
// INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and
@@ -1466,7 +1474,7 @@
int pid = Binder.getCallingPid();
mServiceRecordToPid.put(new Integer(handle), new Integer(pid));
try {
- b.linkToDeath(new Reaper(handle, pid), 0);
+ b.linkToDeath(new Reaper(handle, pid, RFCOMM_RECORD_REAPER), 0);
} catch (RemoteException e) {}
return handle;
}
@@ -1489,20 +1497,85 @@
}
private class Reaper implements IBinder.DeathRecipient {
- int pid;
- int handle;
- Reaper(int handle, int pid) {
- this.pid = pid;
- this.handle = handle;
+ int mPid;
+ int mHandle;
+ int mType;
+
+ Reaper(int handle, int pid, int type) {
+ mPid = pid;
+ mHandle = handle;
+ mType = type;
}
+
+ Reaper(int pid, int type) {
+ mPid = pid;
+ mType = type;
+ }
+
+ @Override
public void binderDied() {
synchronized (BluetoothService.this) {
- if (DBG) Log.d(TAG, "Tracked app " + pid + " died");
- checkAndRemoveRecord(handle, pid);
+ if (DBG) Log.d(TAG, "Tracked app " + mPid + " died" + "Type:" + mType);
+ if (mType == RFCOMM_RECORD_REAPER) {
+ checkAndRemoveRecord(mHandle, mPid);
+ } else if (mType == STATE_CHANGE_REAPER) {
+ mStateChangeTracker.remove(mPid);
+ }
}
}
}
+
+ @Override
+ public boolean changeApplicationBluetoothState(boolean on,
+ IBluetoothStateChangeCallback callback, IBinder binder) {
+ int pid = Binder.getCallingPid();
+ //mStateChangeTracker is a synchronized map
+ if (!mStateChangeTracker.containsKey(pid)) {
+ if (on) {
+ mStateChangeTracker.put(pid, callback);
+ } else {
+ return false;
+ }
+ } else if (!on) {
+ mStateChangeTracker.remove(pid);
+ }
+
+ if (binder != null) {
+ try {
+ binder.linkToDeath(new Reaper(pid, STATE_CHANGE_REAPER), 0);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ int type;
+ if (on) {
+ type = BluetoothAdapterStateMachine.PER_PROCESS_TURN_ON;
+ } else {
+ type = BluetoothAdapterStateMachine.PER_PROCESS_TURN_OFF;
+ }
+
+ mBluetoothState.sendMessage(type, callback);
+ return true;
+ }
+
+ boolean isApplicationStateChangeTrackerEmpty() {
+ return mStateChangeTracker.isEmpty();
+ }
+
+ void clearApplicationStateChangeTracker() {
+ mStateChangeTracker.clear();
+ }
+
+ Collection<IBluetoothStateChangeCallback> getApplicationStateChangeCallbacks() {
+ return mStateChangeTracker.values();
+ }
+
+ int getNumberOfApplicationStateChangeTrackers() {
+ return mStateChangeTracker.size();
+ }
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {