diff options
Diffstat (limited to 'core/java/android/server')
| -rw-r--r-- | core/java/android/server/BluetoothAdapterStateMachine.java | 234 | ||||
| -rw-r--r-- | core/java/android/server/BluetoothEventLoop.java | 4 | ||||
| -rwxr-xr-x | core/java/android/server/BluetoothService.java | 91 |
3 files changed, 271 insertions, 58 deletions
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) { |
