diff options
| author | Jaikumar Ganesh <jaikumar@google.com> | 2011-07-21 18:13:38 -0700 |
|---|---|---|
| committer | Jaikumar Ganesh <jaikumar@google.com> | 2011-07-26 14:49:36 -0700 |
| commit | ef2cb7c93a99096799d415e721dda46d1bf7a005 (patch) | |
| tree | 133fb82001d51efe2ed84e8c17015ada855b99aa /core/java | |
| parent | 070ce94b9fd91d1bb22161a85178440cb46d8ece (diff) | |
| download | frameworks_base-ef2cb7c93a99096799d415e721dda46d1bf7a005.zip frameworks_base-ef2cb7c93a99096799d415e721dda46d1bf7a005.tar.gz frameworks_base-ef2cb7c93a99096799d415e721dda46d1bf7a005.tar.bz2 | |
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
Diffstat (limited to 'core/java')
6 files changed, 365 insertions, 58 deletions
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 @@ public final class BluetoothAdapter { } } + /** + * 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 @@ interface IBluetooth 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 @@ import java.io.PrintWriter; /** * 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 @@ import java.io.PrintWriter; * 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 @@ final class BluetoothAdapterStateMachine extends StateMachine { // 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 @@ final class BluetoothAdapterStateMachine extends StateMachine { 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 @@ final class BluetoothAdapterStateMachine extends StateMachine { 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 @@ final class BluetoothAdapterStateMachine extends StateMachine { 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 @@ final class BluetoothAdapterStateMachine extends StateMachine { * 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 @@ final class BluetoothAdapterStateMachine extends StateMachine { 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 @@ final class BluetoothAdapterStateMachine extends StateMachine { 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 @@ final class BluetoothAdapterStateMachine extends StateMachine { // 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 @@ final class BluetoothAdapterStateMachine extends StateMachine { * 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 @@ final class BluetoothAdapterStateMachine extends StateMachine { 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 @@ final class BluetoothAdapterStateMachine extends StateMachine { 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 @@ final class BluetoothAdapterStateMachine extends StateMachine { 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 @@ final class BluetoothAdapterStateMachine extends StateMachine { 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 @@ final class BluetoothAdapterStateMachine extends StateMachine { 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 @@ final class BluetoothAdapterStateMachine extends StateMachine { } 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 @@ final class BluetoothAdapterStateMachine extends StateMachine { @Override public void enter() { if (DBG) log("Enter BluetoothOn: " + mPersistBluetoothOff); - mPersistBluetoothOff = false; } @Override public boolean processMessage(Message message) { @@ -425,22 +452,40 @@ final class BluetoothAdapterStateMachine extends StateMachine { switch(message.what) { case USER_TURN_OFF: if ((Boolean) message.obj) { - mPersistBluetoothOff = true; + persistSwitchSetting(false); + } + + if (mBluetoothService.isDiscovering()) { + mBluetoothService.cancelDiscovery(); + } + if (!mBluetoothService.isApplicationStateChangeTrackerEmpty()) { + transitionTo(mPerProcessState); + deferMessage(obtainMessage(USER_TURN_OFF)); + break; } - // let it fall through to AIRPLANE_MODE_ON + //$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 @@ final class BluetoothAdapterStateMachine extends StateMachine { } + + 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 @@ final class BluetoothAdapterStateMachine extends StateMachine { 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 @@ class BluetoothEventLoop { 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.BluetoothUuid; 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.FileDescriptor; 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 @@ public class BluetoothService extends IBluetooth.Stub { 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 @@ public class BluetoothService extends IBluetooth.Stub { 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 @@ public class BluetoothService extends IBluetooth.Stub { 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 @@ public class BluetoothService extends IBluetooth.Stub { } 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) { |
