OSDN Git Service

Fixed Bluetooth on/off issues. Removed singleton instances causing issues when servic...
authorfredc <fredc@broadcom.com>
Thu, 26 Apr 2012 01:27:04 +0000 (18:27 -0700)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Tue, 17 Jul 2012 05:04:06 +0000 (22:04 -0700)
Change-Id: Ie1ff4284f507c7da102569f3c0acfd55612a5f6b

src/com/android/bluetooth/btservice/AdapterApp.java
src/com/android/bluetooth/btservice/AdapterProperties.java
src/com/android/bluetooth/btservice/AdapterService.java
src/com/android/bluetooth/btservice/AdapterState.java
src/com/android/bluetooth/btservice/BondStateMachine.java
src/com/android/bluetooth/btservice/JniCallbacks.java
src/com/android/bluetooth/btservice/ProfileService.java
src/com/android/bluetooth/btservice/RemoteDevices.java
src/com/android/bluetooth/opp/BluetoothOppRfcommListener.java
src/com/android/bluetooth/opp/BluetoothOppService.java
src/com/android/bluetooth/pbap/BluetoothPbapService.java

index 9116072..b419fcd 100644 (file)
@@ -9,18 +9,16 @@
 package com.android.bluetooth.btservice;
 
 import android.app.Application;
-import android.bluetooth.BluetoothAdapter;
-import android.content.IntentFilter;
 import android.util.Log;
 
 public class AdapterApp extends Application {
     private static final String TAG = "BluetoothAdapterApp";
     private static final boolean DBG = true;
 
-    static final String BLUETOOTH_ADMIN_PERM =
-        android.Manifest.permission.BLUETOOTH_ADMIN;
-    static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
-
+    static {
+        if (DBG) Log.d(TAG,"Loading JNI Library");
+        System.loadLibrary("bluetooth_jni");
+    }
 
     @Override
     public void onCreate() {
index 4d40a72..aea4e5b 100755 (executable)
@@ -38,39 +38,39 @@ class AdapterProperties {
     private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
     private int mState = BluetoothAdapter.STATE_OFF;
 
-    private static AdapterProperties sInstance;
-    private BluetoothAdapter mAdapter;
+    //private static AdapterProperties sInstance;
     private AdapterService mService;
     private Context mContext;
     private boolean mDiscovering;
-    private final RemoteDevices mRemoteDevices;
+    private RemoteDevices mRemoteDevices;
 
     // Lock for all getters and setters.
     // If finer grained locking is needer, more locks
     // can be added here.
     private Object mObject = new Object();
 
-    private AdapterProperties(AdapterService service, Context context) {
-        mAdapter = BluetoothAdapter.getDefaultAdapter();
+    AdapterProperties(AdapterService service, Context context) {
         mService = service;
         mContext = context;
-        mProfileConnectionState = new HashMap<Integer, Pair<Integer, Integer>>();
-        mRemoteDevices = RemoteDevices.getInstance(service, context);
     }
 
-    static synchronized AdapterProperties getInstance(AdapterService service, Context context) {
-        if (sInstance == null)  {
-            sInstance = new AdapterProperties(service, context);
+    public void init(RemoteDevices remoteDevices) {
+        if (mProfileConnectionState ==null) {
+            mProfileConnectionState = new HashMap<Integer, Pair<Integer, Integer>>();
         } else {
-            sInstance.mService = service;
-            sInstance.mContext = context;
-            //Cleanup needed?
+            mProfileConnectionState.clear();
         }
-        return sInstance;
+        mRemoteDevices = remoteDevices;
     }
 
-    public void init() {
-        mProfileConnectionState.clear();
+    public void cleanup() {
+        if (mProfileConnectionState != null) {
+            mProfileConnectionState.clear();
+            mProfileConnectionState = null;
+        }
+        mRemoteDevices = null;
+        mService = null;
+        mContext = null;
     }
 
     public Object Clone() throws CloneNotSupportedException {
@@ -180,6 +180,7 @@ class AdapterProperties {
      */
     void setState(int mState) {
         synchronized (mObject) {
+            Log.d(TAG,"Setting state to " + mState);
             this.mState = mState;
         }
     }
@@ -189,6 +190,7 @@ class AdapterProperties {
      */
     int getState() {
         synchronized (mObject) {
+            Log.d(TAG,"State = " + mState);
             return mState;
         }
     }
@@ -406,6 +408,10 @@ class AdapterProperties {
                         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                         mContext.sendBroadcast(intent, mService.BLUETOOTH_PERM);
                         debugLog("Scan Mode:" + mScanMode);
+                        if (mBluetoothDisabling) {
+                            mBluetoothDisabling=false;
+                            mService.startBluetoothDisable();
+                        }
                         break;
                     case AbstractionLayer.BT_PROPERTY_UUIDS:
                         mUuids = Utils.byteArrayToUuid(val);
@@ -440,6 +446,9 @@ class AdapterProperties {
     }
 
     void onBluetoothReady() {
+        Log.d(TAG, "ScanMode =  " + mScanMode );
+        Log.d(TAG, "State =  " + getState() );
+
         // When BT is being turned on, all adapter properties will be sent in 1
         // callback. At this stage, set the scan mode.
         synchronized (mObject) {
@@ -458,9 +467,16 @@ class AdapterProperties {
         }
     }
 
+    private boolean mBluetoothDisabling=false;
+
     void onBluetoothDisable() {
         // When BT disable is invoked, set the scan_mode to NONE
         // so no incoming connections are possible
+
+        //Set flag to indicate we are disabling. When property change of scan mode done
+        //continue with disable sequence
+        debugLog("onBluetoothDisable()");
+        mBluetoothDisabling = true;
         if (getState() == BluetoothAdapter.STATE_TURNING_OFF) {
             setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE);
         }
index 4220fa7..b78da08 100755 (executable)
@@ -74,37 +74,31 @@ public class AdapterService extends Service {
         android.Manifest.permission.BLUETOOTH_ADMIN;
     static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
 
+    private static final int ADAPTER_SERVICE_TYPE=Service.START_STICKY;
+
+    static {
+        classInitNative();
+    }
+
+    private static AdapterService sAdapterService;
+    public static AdapterService getAdapterService(){
+        return sAdapterService;
+    }
+
     private IBluetoothManager mBluetoothManager;
     private IBluetooth mBluetoothService;
     private AdapterProperties mAdapterProperties;
     private int mAdapterState;
     private Context mContext;
-    private static AdapterService sAdapterService;
 
     private AdapterState mAdapterStateMachine;
     private BondStateMachine mBondStateMachine;
     private JniCallbacks mJniCallbacks;
     private RemoteDevices mRemoteDevices;
-    private boolean mStopPending;
-    private boolean mStartPending;
     private boolean mProfilesStarted;
     private boolean mNativeAvailable;
     private HashMap<String,Integer> mProfileServicesState = new HashMap<String,Integer>();
-
-    //In case Bluetooth app crashes, we need to reinit JNI
-    private static boolean mIsJniInited;
-    private static synchronized void initJni() {
-        if (!mIsJniInited) {
-            if (DBG) Log.d(TAG,"Initializing JNI Library");
-            System.loadLibrary("bluetooth_jni");
-            classInitNative();
-            mIsJniInited=true;
-        }
-    }
-
-    public static AdapterService getAdapterService(){
-        return sAdapterService;
-    }
+    private int mCurrentRequestId;
 
     public void onProfileConnectionStateChanged(BluetoothDevice device, int profileId, int newState, int prevState) {
         Message m = mHandler.obtainMessage(MESSAGE_PROFILE_CONNECTION_STATE_CHANGED);
@@ -136,8 +130,10 @@ public class AdapterService extends Service {
     }
 
     private void processProfileServiceStateChanged(String serviceName, int state) {
-        if (DBG) Log.d(TAG,"onProfileServiceStateChange: serviceName=" + serviceName + ", state = " + state);
         boolean doUpdate=false;
+        boolean isTurningOn;
+        boolean isTurningOff;
+
         synchronized (mProfileServicesState) {
             Integer prevState = mProfileServicesState.get(serviceName);
             if (prevState != null && prevState != state) {
@@ -145,10 +141,18 @@ public class AdapterService extends Service {
                 doUpdate=true;
             }
         }
+        if (DBG) Log.d(TAG,"onProfileServiceStateChange: serviceName=" + serviceName + ", state = " + state +", doUpdate = " + doUpdate);
+
         if (!doUpdate) {
             return;
         }
-        if (mStopPending) {
+
+        synchronized (mAdapterStateMachine) {
+            isTurningOff = mAdapterStateMachine.isTurningOff();
+            isTurningOn = mAdapterStateMachine.isTurningOn();
+        }
+
+        if (isTurningOff) {
             //Process stop or disable pending
             //Check if all services are stopped if so, do cleanup
             //if (DBG) Log.d(TAG,"Checking if all profiles are stopped...");
@@ -157,14 +161,14 @@ public class AdapterService extends Service {
                 while (i.hasNext()) {
                     Map.Entry<String,Integer> entry = i.next();
                     if (BluetoothAdapter.STATE_OFF != entry.getValue()) {
-                        //Log.d(TAG, "Profile still running: " entry.getKey());
+                        Log.d(TAG, "Profile still running: " + entry.getKey());
                         return;
                     }
                 }
             }
             if (DBG) Log.d(TAG, "All profile services stopped...");
-            processStopped();
-        } else if (mStartPending) {
+            onProfilesStopped();
+        } else if (isTurningOn) {
             //Process start pending
             //Check if all services are started if so, update state
             //if (DBG) Log.d(TAG,"Checking if all profiles are running...");
@@ -173,33 +177,28 @@ public class AdapterService extends Service {
                 while (i.hasNext()) {
                     Map.Entry<String,Integer> entry = i.next();
                     if (BluetoothAdapter.STATE_ON != entry.getValue()) {
-                        //Log.d(TAG, "Profile still not running:" + entry.getKey());
+                        Log.d(TAG, "Profile still not running:" + entry.getKey());
                         return;
                     }
                 }
             }
             if (DBG) Log.d(TAG, "All profile services started.");
-            processStarted();
+            onProfilesStarted();
         }
     }
 
     @Override
     public void onCreate() {
         super.onCreate();
-        if (DBG) Log.d(TAG, "onCreate");
+        if (DBG) debugLog("onCreate");
         mContext = this;
         sAdapterService = this;
-        initJni(); //We always check and init JNI in case we crashed and restarted
-        mRemoteDevices = RemoteDevices.getInstance(this, mContext);
-        mRemoteDevices.init();
-        mAdapterProperties = AdapterProperties.getInstance(this, mContext);
-        mAdapterProperties.init();
+        mAdapterProperties = new AdapterProperties(this, mContext);
         mAdapterStateMachine =  new AdapterState(this, mContext, mAdapterProperties);
-        mBondStateMachine = new BondStateMachine(this, mContext, mAdapterProperties);
-        mJniCallbacks = JniCallbacks.getInstance(mRemoteDevices, mAdapterProperties,
-                                                 mAdapterStateMachine, mBondStateMachine);
+        mJniCallbacks = JniCallbacks.getInstance(null, mAdapterProperties,mAdapterStateMachine,null);
         initNative();
         mNativeAvailable=true;
+        mAdapterStateMachine.start();
 
         //Load the name and address
         getAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_BDADDR);
@@ -208,75 +207,89 @@ public class AdapterService extends Service {
 
     @Override
     public IBinder onBind(Intent intent) {
-        if (DBG) Log.d(TAG, "onBind");
+        if (DBG) debugLog("onBind");
         return mBinder;
     }
     public boolean onUnbind(Intent intent) {
-        if (DBG) Log.d(TAG,"onUnbind");
+        if (DBG) debugLog("onUnbind");
         return super.onUnbind(intent);
     }
 
     public void onDestroy() {
-        if (DBG) Log.d(TAG, "onDestroy()");
-        super.onDestroy();
+        debugLog("****onDestroy()********");
+        mHandler.removeMessages(MESSAGE_SHUTDOWN);
+        cleanup();
     }
 
     public int onStartCommand(Intent intent ,int flags, int startId) {
-        if (DBG) Log.d(TAG, "onStartCommand");
+
+        mCurrentRequestId = startId;
+        if (DBG) debugLog("onStartCommand: flags = " + flags + ", startId = " + startId);
         if (checkCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM)!=PackageManager.PERMISSION_GRANTED) {
             Log.e(TAG, "Permission denied!");
-            return START_STICKY;
+            return ADAPTER_SERVICE_TYPE;
+        }
+
+        //Check if we are restarting
+        if (intent == null) {
+            debugLog("Restarting AdapterService");
+            return ADAPTER_SERVICE_TYPE;
         }
 
+        //Get action and check if valid. If invalid, ignore and return
         String action  = intent.getStringExtra(EXTRA_ACTION);
-        if (DBG) Log.d(TAG,"onStartCommand(): action = " + action);
-        if (ACTION_SERVICE_STATE_CHANGED.equals(action)) {
-            int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.ERROR);
-            if (DBG) Log.d(TAG,"onStartCommand(): state = " + Utils.debugGetAdapterStateString(state));
-            if (state == BluetoothAdapter.STATE_OFF) {
-                Message m = mHandler.obtainMessage(MESSAGE_STOP);
-                mHandler.sendMessage(m);
-            } else if (state == BluetoothAdapter.STATE_ON) {
-                Message m = mHandler.obtainMessage(MESSAGE_START);
-                mHandler.sendMessage(m);
-            }
+        debugLog("onStartCommand(): action = " + action);
+        if (!ACTION_SERVICE_STATE_CHANGED.equals(action)) {
+            Log.w(TAG,"Unknown action: " + action);
+            return ADAPTER_SERVICE_TYPE;
         }
-        return START_STICKY;
-    }
 
-    final private IBluetoothManagerCallback mManagerCallback =
-            new IBluetoothManagerCallback.Stub() {
-                public void onBluetoothServiceUp(IBluetooth bluetoothService) {
-                    if (DBG) Log.d(TAG, "onBluetoothServiceUp");
-                    synchronized (mManagerCallback) {
-                        mBluetoothService = bluetoothService;
-                    }
-                }
+        //Check state of request. If invalid, ignore and return
+        int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.ERROR);
+        debugLog("onStartCommand(): state = " + Utils.debugGetAdapterStateString(state));
 
-                public void onBluetoothServiceDown() {
-                    if (DBG) Log.d(TAG, "onBluetoothServiceDown");
-                    synchronized (mManagerCallback) {
-                        mBluetoothService = null;
-                    }
-                }
-        };
+        //Cancel any pending shutdown requests
+        synchronized (mHandler) {
+            mHandler.removeMessages(MESSAGE_SHUTDOWN);
+        }
 
-    private void processStart() {
-        if (DBG) Log.d(TAG,"start() called, mStartPending= " + mStartPending + ", mProfilesStarted=" + mProfilesStarted);
-        if (mProfilesStarted || mStartPending) return;
-        if (DBG) Log.d(TAG,"starting bluetooth state machine and profiles..");
+        if (state == BluetoothAdapter.STATE_OFF) {
+            Message m = mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_OFF);
+            m.arg1= startId;
+            mAdapterStateMachine.sendMessage(m);
+        } else if (state == BluetoothAdapter.STATE_ON) {
+            Message m = mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_ON);
+            m.arg1= startId;
+            mAdapterStateMachine.sendMessage(m);
+        } else {
+            Log.w(TAG,"Invalid state: " + action);
+            return ADAPTER_SERVICE_TYPE;
+        }
+        return ADAPTER_SERVICE_TYPE;
+    }
 
-        mStartPending=true;
+    void processStart() {
+        if (DBG) debugLog("processStart()");
 
-        mAdapterStateMachine.start();
-        mBondStateMachine.start();
+        //Initialize data objects
+        for (int i=0; i < SUPPORTED_PROFILE_SERVICES.length;i++) {
+            mProfileServicesState.put(SUPPORTED_PROFILE_SERVICES[i].getName(),BluetoothAdapter.STATE_OFF);
+        }
+
+        mRemoteDevices = new RemoteDevices(this, mContext);
+        mRemoteDevices.init();
+        mAdapterProperties.init(mRemoteDevices);
+        mBondStateMachine = new BondStateMachine(this, mContext, mAdapterProperties, mRemoteDevices);
+        mJniCallbacks.init(mRemoteDevices, mAdapterProperties,mAdapterStateMachine,mBondStateMachine);
 
-        //Get bluetooth service for profiles
+        //Init BluetoothManager
+        if (DBG) {debugLog("processStart(): Initializing Bluetooth Manager");}
         IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE);
         if (b != null) {
             mBluetoothManager = IBluetoothManager.Stub.asInterface(b);
             if (mBluetoothManager != null) {
                 try {
+                    Log.d(TAG, "FRED: Registering manager callback " + mManagerCallback);
                     mBluetoothService = mBluetoothManager.registerAdapter(mManagerCallback);
                 } catch (RemoteException re) {
                     Log.e(TAG, "",re);
@@ -284,190 +297,153 @@ public class AdapterService extends Service {
             }
         }
 
+        //Start Bond State Machine
+        if (DBG) {debugLog("processStart(): Starting Bond State Machine");}
+        mBondStateMachine.start();
+
         //Start profile services
         if (SUPPORTED_PROFILE_SERVICES.length==0 || mProfilesStarted) {
             //Skip starting profiles and go to next step
-            processStarted();
+            if (DBG) {debugLog("processStart(): Profile Services started");}
+            Message m = mAdapterStateMachine.obtainMessage(AdapterState.STARTED);
+            mAdapterStateMachine.sendMessage(m);
         } else {
-            mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_START_TIMEOUT), MAX_TIMEOUT_MS);
-            for (int i=0; i <SUPPORTED_PROFILE_SERVICES.length;i++) {
-                startProfileService(SUPPORTED_PROFILE_SERVICES[i]);
-            }
+            //Startup all profile services
+            setProfileServiceState(SUPPORTED_PROFILE_SERVICES,BluetoothAdapter.STATE_ON);
         }
     }
 
-    private void processStartTimeout(){
-        //FIXME:
-        Log.e(TAG,"************Start Timed out!!!*******************");
-        mStartPending = false;
-        sendIntent(BluetoothAdapter.STATE_OFF);
-        //Stop
-    }
-
-    private void processStarted() {
-        Log.d(TAG, "processStarted()");
-        mHandler.removeMessages(MESSAGE_START_TIMEOUT);
-        mProfilesStarted = true;
-        //Enable bluetooth
-        Message m = mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_ON);
+    void startBluetoothDisable() {
+        Log.d(TAG,"startBluetoothDisable()");
+        Message m = mAdapterStateMachine.obtainMessage(AdapterState.BEGIN_DISABLE);
         mAdapterStateMachine.sendMessage(m);
     }
 
-    void onBluetoothEnableTimeout() {
-        Log.e(TAG, "Timeout occurred while enabling bluetooth...");
-        sendIntent(BluetoothAdapter.STATE_OFF);
-        //FIXME: stop services and cleanup
-    }
-
-    void onBluetoothEnabled() {
-        Log.d(TAG, "onBluetoothEnabled()");
-        Message m = mHandler.obtainMessage(MESSAGE_ENABLED);
-        mHandler.sendMessage(m);
+    void onProfilesStarted(){
+        Log.d(TAG,"onProfilesStarted()");
+        mProfilesStarted=true;
+        Message m = mAdapterStateMachine.obtainMessage(AdapterState.STARTED);
+        mAdapterStateMachine.sendMessage(m);
     }
 
-    void processEnabled() {
-        mStartPending = false;
-        mStopPending=false;
-        sendIntent(BluetoothAdapter.STATE_ON);
+    void onProfilesStopped() {
+        Log.d(TAG,"onProfilesStopped()");
+        mProfilesStarted=false;
+        //Message m = mAdapterStateMachine.obtainMessage(AdapterState.DISABLE);
+        Message m = mAdapterStateMachine.obtainMessage(AdapterState.STOPPED);
+        mAdapterStateMachine.sendMessage(m);
     }
 
-    //Called as part of disable or independently (when just getting address)
-    private void processStop() {
-        if (DBG) Log.d(TAG,"stop() called, mStopPending= " + mStopPending + ", mProfilesStarted=" + mProfilesStarted);
-        if (mStopPending) return;
-        mStopPending=true;
 
+    boolean stopProfileServices() {
         if (SUPPORTED_PROFILE_SERVICES.length==0 || !mProfilesStarted) {
-            cleanup();
-        } else {
-            Message m = mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_OFF);
-            mAdapterStateMachine.sendMessage(m);
+            if (DBG) {debugLog("processDisable(): No profiles services to stop or already stopped.");}
+            return false;
         }
+        setProfileServiceState(SUPPORTED_PROFILE_SERVICES,BluetoothAdapter.STATE_OFF);
+        return true;
     }
 
-    void onBluetoothDisabled() {
-        Log.d(TAG, "onBluetoothDisabled()");
-        Message m = mHandler.obtainMessage(MESSAGE_DISABLED);
-        mHandler.sendMessage(m);
-    }
-
-    void onBluetoothDisableTimeout() {
-        Log.e(TAG, "Timeout occurred while disable bluetooth...");
-        Message m = mHandler.obtainMessage(MESSAGE_DISABLED);
-        mHandler.sendMessage(m);
-    }
-
-    private void processDisabled() {
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_STOP_TIMEOUT), MAX_TIMEOUT_MS);
-        for (int i=SUPPORTED_PROFILE_SERVICES.length-1; i >=0;i--) {
-                stopProfileService(SUPPORTED_PROFILE_SERVICES[i]);
-        }
-    }
-
-    private void processStopTimeout() {
-        //FIXME:
-        Log.e(TAG,"************Stop Timed out!!!*******************");
-        processStopped();
-    }
-
-    private void processStopped() {
+    void processStopped() {
         Log.d(TAG, "processStopped()");
-        mHandler.removeMessages(MESSAGE_STOP_TIMEOUT);
 
         if (mBluetoothManager != null) {
             try {
+                Log.d(TAG,"FRED: Unregistering manager callback " + mManagerCallback);
                 mBluetoothManager.unregisterAdapter(mManagerCallback);
             } catch (RemoteException re) {
                 Log.e(TAG, "",re);
             }
         }
         mBondStateMachine.quit();
-        mAdapterStateMachine.quit();
-        sendIntent(BluetoothAdapter.STATE_OFF);
-        cleanup();
+        mBondStateMachine.cleanup();
+        mBondStateMachine = null;
     }
 
-    void cleanup () {
-        if (DBG) Log.d(TAG, "cleanup()");
-
-        if (mBondStateMachine != null) {
-            mBondStateMachine.cleanup();
-            mBondStateMachine = null;
+    void startShutdown(int requestId) {
+        debugLog("startShutdown(): requestId = " + requestId + ", currentRequestId=" + mCurrentRequestId);
+        if (requestId <0) {
+            Log.w(TAG, "Ignoring shutdown request. Invalid requestId");
+            return;
         }
-        if (mAdapterStateMachine != null) {
-            mAdapterStateMachine.cleanup();
-            mAdapterStateMachine = null;
+
+        Message m = mHandler.obtainMessage(MESSAGE_SHUTDOWN);
+        synchronized(mHandler) {
+            mHandler.sendMessageDelayed(m, SHUTDOWN_TIMEOUT);
         }
+        stopSelfResult(requestId);
+    }
+
+    void cleanup () {
+        if (DBG)debugLog("cleanup()");
+
         if (mNativeAvailable) {
             Log.d(TAG, "Cleaning up adapter native....");
             cleanupNative();
             Log.d(TAG, "Done cleaning up adapter native....");
             mNativeAvailable=false;
         }
-        mStartPending = false;
-        mStopPending=false;
-        mProfilesStarted = false;
-        stopSelf();
-    }
 
-    private void sendIntent(int newState) {
-        int oldState = mAdapterProperties.getState();
-        Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
-        intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, oldState);
-        intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        mAdapterProperties.setState(newState);
-        mContext.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM);
-        //infoLog("Bluetooth State Change Intent: " + oldState + " -> " + newState);
+        if (mAdapterStateMachine != null) {
+            mAdapterStateMachine.quit();
+            mAdapterStateMachine.cleanup();
+            mAdapterStateMachine = null;
+        }
+
+        if (mRemoteDevices != null) {
+            mRemoteDevices.cleanup();
+            mRemoteDevices = null;
+        }
+
+        if (mAdapterProperties != null) {
+            mAdapterProperties.cleanup();
+            mAdapterProperties = null;
+        }
+
+        mProfileServicesState.clear();
+        if (DBG)debugLog("cleanup() done");
     }
 
+    final private IBluetoothManagerCallback mManagerCallback =
+            new IBluetoothManagerCallback.Stub() {
+                public void onBluetoothServiceUp(IBluetooth bluetoothService) {
+                    if (DBG) Log.d(TAG, "onBluetoothServiceUp");
+                    synchronized (mManagerCallback) {
+                        mBluetoothService = bluetoothService;
+                    }
+                }
+
+                public void onBluetoothServiceDown() {
+                    if (DBG) Log.d(TAG, "onBluetoothServiceDown " + this);
+                    synchronized (mManagerCallback) {
+                        mBluetoothService = null;
+                    }
+                }
+        };
+
     private static final int MESSAGE_PROFILE_SERVICE_STATE_CHANGED =1;
-    private static final int MESSAGE_START=2;
-    private static final int MESSAGE_STOP=3;
-    private static final int MESSAGE_ENABLED=4;
-    private static final int MESSAGE_DISABLED=5;
-    private static final int MESSAGE_START_TIMEOUT=10;
-    private static final int MESSAGE_STOP_TIMEOUT=11;
     private static final int MESSAGE_PROFILE_CONNECTION_STATE_CHANGED=20;
-    private static final int MAX_TIMEOUT_MS=3000;
+    private static final int MESSAGE_SHUTDOWN= 100;
+    private static final int SHUTDOWN_TIMEOUT=2000;
 
     private final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
-            if (DBG) Log.d (TAG, "Message: " + msg.what);
+            if (DBG) debugLog("Message: " + msg.what);
 
             switch (msg.what) {
-                case MESSAGE_START: {
-                    processStart();
-                }
-                    break;
-                case MESSAGE_START_TIMEOUT: {
-                    processStartTimeout();
-                }
-                    break;
-                case MESSAGE_ENABLED: {
-                    processEnabled();
-                }
-                    break;
-                case MESSAGE_STOP: {
-                    processStop();
-                }
-                    break;
-                case MESSAGE_STOP_TIMEOUT: {
-                    processStopTimeout();
-                }
-                    break;
-                case MESSAGE_DISABLED: {
-                    processDisabled();
+                case MESSAGE_SHUTDOWN: {
+                    if (DBG) Log.d(TAG,"***SHUTDOWN: TIMEOUT!!! Forcing shutdown...");
+                    stopSelf();
                 }
                     break;
                 case MESSAGE_PROFILE_SERVICE_STATE_CHANGED: {
-                    Log.d(TAG, "MESSAGE_PROFILE_SERVICE_STATE_CHANGED");
+                    if(DBG) debugLog("MESSAGE_PROFILE_SERVICE_STATE_CHANGED");
                     processProfileServiceStateChanged((String) msg.obj, msg.arg1);
                 }
                     break;
                 case MESSAGE_PROFILE_CONNECTION_STATE_CHANGED: {
-                    Log.d(TAG, "MESSAGE_PROFILE_CONNECTION_STATE_CHANGED");
+                    if (DBG) debugLog( "MESSAGE_PROFILE_CONNECTION_STATE_CHANGED");
                     processProfileStateChanged((BluetoothDevice) msg.obj, msg.arg1,msg.arg2, msg.getData().getInt("prevState",BluetoothAdapter.ERROR));
                 }
                     break;
@@ -476,36 +452,39 @@ public class AdapterService extends Service {
     };
 
     @SuppressWarnings("rawtypes")
-    private void startProfileService(Class service) {
-        String serviceName = service.getName();
-        Integer serviceState = mProfileServicesState.get(serviceName);
-        if(serviceState != null && serviceState != BluetoothAdapter.STATE_OFF) {
-            Log.w(TAG, "Unable to start service "+serviceName+". Invalid state: " + serviceState);
+    private void setProfileServiceState(Class[] services, int state) {
+        if (state != BluetoothAdapter.STATE_ON && state != BluetoothAdapter.STATE_OFF) {
+            Log.w(TAG,"setProfileServiceState(): invalid state...Leaving...");
             return;
         }
 
-        mProfileServicesState.put(serviceName,BluetoothAdapter.STATE_TURNING_ON);
-        Intent i = new Intent(this,service);
-        //i.putExtra(EXTRA_ACTION,ACTION_SERVICE_STATE_CHANGED);
-        //i.putExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.STATE_ON);
-        if (DBG) Log.d(TAG, "Starting profile service "+serviceName);
-        startService(i);
-    }
+        int expectedCurrentState= BluetoothAdapter.STATE_OFF;
+        int pendingState = BluetoothAdapter.STATE_TURNING_ON;
+        if (state == BluetoothAdapter.STATE_OFF) {
+            expectedCurrentState= BluetoothAdapter.STATE_ON;
+            pendingState = BluetoothAdapter.STATE_TURNING_OFF;
+        }
 
-    @SuppressWarnings("rawtypes")
-    private void stopProfileService(Class service) {
-        String serviceName = service.getName();
-        Integer serviceState = mProfileServicesState.get(service.getName());
-        if(serviceState == null || serviceState != BluetoothAdapter.STATE_ON) {
-            Log.w(TAG, "Unable to stop service " + serviceName + ". Invalid state: " + serviceState);
-            return;
+        for (int i=0; i <services.length;i++) {
+            String serviceName = services[i].getName();
+            Integer serviceState = mProfileServicesState.get(serviceName);
+            if(serviceState != null && serviceState != expectedCurrentState) {
+                Log.w(TAG, "Unable to " + (state == BluetoothAdapter.STATE_OFF? "start" : "stop" ) +" service " +
+                        serviceName+". Invalid state: " + serviceState);
+                continue;
+            }
+
+            if (DBG) {
+                Log.w(TAG, (state == BluetoothAdapter.STATE_OFF? "Stopping" : "Starting" ) +" service " +
+                        serviceName);
+            }
+
+            mProfileServicesState.put(serviceName,pendingState);
+            Intent intent = new Intent(this,services[i]);
+            intent.putExtra(EXTRA_ACTION,ACTION_SERVICE_STATE_CHANGED);
+            intent.putExtra(BluetoothAdapter.EXTRA_STATE,state);
+            startService(intent);
         }
-        mProfileServicesState.put(serviceName, BluetoothAdapter.STATE_TURNING_OFF);
-        Intent i = new Intent(this, service);
-        i.putExtra(EXTRA_ACTION,ACTION_SERVICE_STATE_CHANGED);
-        i.putExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.STATE_OFF);
-        if (DBG) Log.d(TAG, "Stopping profile service "+serviceName);
-        startService(i);
     }
 
     /**
@@ -519,13 +498,14 @@ public class AdapterService extends Service {
 
         public int getState() {
             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+            debugLog("getState(): mAdapterProperties: " + mAdapterProperties);
             return mAdapterProperties.getState();
         }
 
         public boolean enable() {
             enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                     "Need BLUETOOTH ADMIN permission");
-            Log.d(TAG,"enable() called...");
+            if (DBG) debugLog("enable() called...");
             Message m =
                     mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_ON);
             m.arg1 = 1; //persist state
@@ -536,7 +516,7 @@ public class AdapterService extends Service {
         public boolean disable(boolean persist) {
             enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                     "Need BLUETOOTH ADMIN permission");
-            Log.d(TAG,"disable() called...");
+            if (DBG) debugLog("disable() called...");
             int val = (persist ? 1 : 0);
             Message m =
                     mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_OFF);
@@ -560,7 +540,12 @@ public class AdapterService extends Service {
         public String getName() {
             enforceCallingOrSelfPermission(BLUETOOTH_PERM,
                     "Need BLUETOOTH permission");
-            return mAdapterProperties.getName();
+            try {
+                return mAdapterProperties.getName();
+            } catch (Throwable t) {
+                Log.d(TAG, "Unexpected exception while calling getName()",t);
+            }
+            return null;
         }
 
         public boolean setName(String name) {
@@ -806,12 +791,12 @@ public class AdapterService extends Service {
         return -1;
     }
 
-    private static void debugLog(String msg) {
-        Log.d(TAG, msg);
+    private void debugLog(String msg) {
+        Log.d(TAG +"(" +hashCode()+")", msg);
     }
 
-    private static void errorLog(String msg) {
-        Log.e(TAG, msg);
+    private void errorLog(String msg) {
+        Log.e(TAG +"(" +hashCode()+")", msg);
     }
 
     private native static void classInitNative();
index c0a2c01..f0b4300 100755 (executable)
@@ -28,26 +28,49 @@ final class AdapterState extends StateMachine {
     private static final String TAG = "BluetoothAdapterState";
 
     static final int USER_TURN_ON = 1;
-    static final int USER_TURN_OFF = 2;
-    static final int AIRPLANE_MODE_ON = 3;
-    static final int AIRPLANE_MODE_OFF = 4;
-    static final int ENABLED_READY = 5;
-    static final int DISABLED = 6;
-    static final int ENABLE_TIMEOUT = 7;
+    static final int STARTED=2;
+    static final int ENABLED_READY = 3;
+   // static final int POST_ENABLE =4;
 
-    private static final int ENABLE_TIMEOUT_DELAY = 6000; // 6 secs
+    static final int USER_TURN_OFF = 20;
+    static final int BEGIN_DISABLE = 21;
+    static final int ALL_DEVICES_DISCONNECTED = 22;
+   // static final int DISABLE=23;
+    static final int DISABLED = 24;
+    static final int STOPPED=25;
 
+    static final int START_TIMEOUT = 100;
+    static final int ENABLE_TIMEOUT = 101;
+    static final int DISABLE_TIMEOUT = 103;
+    static final int STOP_TIMEOUT = 104;
+
+    static final int USER_TURN_OFF_DELAY_MS=500;
+
+    //TODO: tune me
+    private static final int ENABLE_TIMEOUT_DELAY = 8000;
+    private static final int DISABLE_TIMEOUT_DELAY = 8000;
+    private static final int START_TIMEOUT_DELAY = 5000;
+    private static final int STOP_TIMEOUT_DELAY = 5000;
+    private static final int PROPERTY_OP_DELAY =2000;
     private AdapterService mAdapterService;
     private Context mContext;
     private AdapterProperties mAdapterProperties;
-    private boolean mPendingPersistEnable;
     private PendingCommandState mPendingCommandState = new PendingCommandState();
     private OnState mOnState = new OnState();
     private OffState mOffState = new OffState();
 
-    public boolean isCurrentlyOn() {
-        return mOnState == getCurrentState();
+    public boolean isTurningOn() {
+        boolean isTurningOn=  mPendingCommandState.isTurningOn();
+        Log.d(TAG,"isTurningOn()=" + isTurningOn);
+        return isTurningOn;
     }
+
+    public boolean isTurningOff() {
+        boolean isTurningOff= mPendingCommandState.isTurningOff();
+        Log.d(TAG,"isTurningOff()=" + isTurningOff);
+        return isTurningOff;
+    }
+
     public AdapterState(AdapterService service, Context context,
             AdapterProperties adapterProperties) {
         super("BluetoothAdapterState:");
@@ -72,7 +95,7 @@ final class AdapterState extends StateMachine {
     private class OffState extends State {
         @Override
         public void enter() {
-            infoLog("Entering Off State");
+            infoLog("Entering OffState");
         }
 
         @Override
@@ -81,27 +104,24 @@ final class AdapterState extends StateMachine {
                 Log.d(TAG, "Received quit request...");
                 return false;
             }
+            int requestId = msg.arg1;
 
             switch(msg.what) {
                case USER_TURN_ON:
-                   int persist = msg.arg1;
-                   //if (persist == 1) mAdapterService.persistBluetoothSetting(true);
-                   //Persist enable state only once enable completes
-                   mPendingPersistEnable = (persist ==1);
+                   if (DBG) Log.d(TAG,"CURRENT_STATE=OFF, MESSAGE = USER_TURN_ON, requestId= " + msg.arg1);
                    sendIntent(BluetoothAdapter.STATE_TURNING_ON);
-                   boolean ret = mAdapterService.enableNative();
-                   if (!ret) {
-                       Log.e(TAG, "Error while turning Bluetooth On");
-                       sendIntent(BluetoothAdapter.STATE_OFF);
-                   } else {
-                       sendMessageDelayed(ENABLE_TIMEOUT, ENABLE_TIMEOUT_DELAY);
-                       transitionTo(mPendingCommandState);
-                   }
+                   mPendingCommandState.setTurningOn(true);
+                   transitionTo(mPendingCommandState);
+                   sendMessageDelayed(START_TIMEOUT, START_TIMEOUT_DELAY);
+                   mAdapterService.processStart();
                    break;
                case USER_TURN_OFF:
+                   if (DBG) Log.d(TAG,"CURRENT_STATE=OFF, MESSAGE = USER_TURN_OFF, requestId= " + msg.arg1);
+                   //Handle case of service started and stopped without enable
+                   mAdapterService.startShutdown(requestId);
                    break;
                default:
-                   Log.e(TAG, "Received unhandled state: " + msg.what);
+                   if (DBG) Log.d(TAG,"ERROR: UNEXPECTED MESSAGE: CURRENT_STATE=OFF, MESSAGE = " + msg.what );
                    return false;
             }
             return true;
@@ -118,26 +138,26 @@ final class AdapterState extends StateMachine {
         public boolean processMessage(Message msg) {
             switch(msg.what) {
                case USER_TURN_OFF:
+                   if (DBG) Log.d(TAG,"CURRENT_STATE=ON, MESSAGE = USER_TURN_OFF, requestId= " + msg.arg1);
                    sendIntent(BluetoothAdapter.STATE_TURNING_OFF);
+                   mPendingCommandState.setTurningOff(true);
+                   mPendingCommandState.setOffRequestId(msg.arg1);
+                   transitionTo(mPendingCommandState);
+
                    // Invoke onBluetoothDisable which shall trigger a
                    // setScanMode to SCAN_MODE_NONE
+                   Message m = obtainMessage(BEGIN_DISABLE);
+                   m.arg1 = msg.arg1;
+                   sendMessageDelayed(m, PROPERTY_OP_DELAY);
                    mAdapterProperties.onBluetoothDisable();
-
-                   // stack takes care of disconnecting profiles, send disable here
-                   boolean ret = mAdapterService.disableNative();
-                   if (!ret) {
-                       Log.e(TAG, "Error while turning Bluetooth Off");
-                       sendIntent(BluetoothAdapter.STATE_ON);
-                   } else {
-                       transitionTo(mPendingCommandState);
-                   }
                    break;
+
                case USER_TURN_ON:
-               //case AIRPLANE_MODE_OFF:
-                   //ignore
+                   if (DBG) Log.d(TAG,"CURRENT_STATE=ON, MESSAGE = USER_TURN_ON, requestId= " + msg.arg1);
+                   Log.i(TAG,"Bluetooth already ON, ignoring USER_TURN_ON");
                    break;
                default:
-                   Log.e(TAG, "Received unhandled state: " + msg.what);
+                   if (DBG) Log.d(TAG,"ERROR: UNEXPECTED MESSAGE: CURRENT_STATE=ON, MESSAGE = " + msg.what );
                    return false;
             }
             return true;
@@ -145,37 +165,154 @@ final class AdapterState extends StateMachine {
     }
 
     private class PendingCommandState extends State {
-        @Override
+        private boolean mIsTurningOn;
+        private boolean mIsTurningOff;
+
+        private int mRequestId;
+
         public void enter() {
-            infoLog("Entering PendingCommandState State");
+            infoLog("Entering PendingCommandState State: isTurningOn()=" + isTurningOn() + ", isTurningOff()=" + isTurningOff());
+        }
+
+        public void setTurningOn(boolean isTurningOn) {
+            mIsTurningOn = isTurningOn;
+        }
+
+        public boolean isTurningOn() {
+            return mIsTurningOn;
+        }
+
+        public void setTurningOff(boolean isTurningOff) {
+            mIsTurningOff = isTurningOff;
+        }
+
+        public boolean isTurningOff() {
+            return mIsTurningOff;
+        }
+
+        public void setOffRequestId(int requestId) {
+            mRequestId = requestId;
+        }
+
+        public int getOffRequestId() {
+            return mRequestId;
         }
 
         @Override
         public boolean processMessage(Message msg) {
+            boolean isTurningOn= isTurningOn();
+            boolean isTurningOff = isTurningOff();
             switch (msg.what) {
                 case USER_TURN_ON:
+                    if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = USER_TURN_ON, requestId= " + msg.arg1
+                            + ", isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
+                    if (isTurningOn) {
+                        Log.i(TAG,"CURRENT_STATE=PENDING: Alreadying turning on bluetooth... Ignoring USER_TURN_ON...");
+                    } else {
+                        Log.i(TAG,"CURRENT_STATE=PENDING: Deferring request USER_TURN_ON");
+                        deferMessage(msg);
+                    }
+                    break;
                 case USER_TURN_OFF:
-                case AIRPLANE_MODE_ON:
-                case AIRPLANE_MODE_OFF:
-                    deferMessage(msg);
+                    if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = USER_TURN_ON, requestId= " + msg.arg1
+                            + ", isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
+                    if (isTurningOff) {
+                        Log.i(TAG,"CURRENT_STATE=PENDING: Alreadying turning off bluetooth... Ignoring USER_TURN_OFF...");
+                    } else {
+                        Log.i(TAG,"CURRENT_STATE=PENDING: Deferring request USER_TURN_OFF");
+                        deferMessage(msg);
+                    }
                     break;
+                case STARTED:   {
+                    if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = STARTED, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
+                    //Remove start timeout
+                    removeMessages(START_TIMEOUT);
+
+                    //Enable
+                    boolean ret = mAdapterService.enableNative();
+                    if (!ret) {
+                        Log.e(TAG, "Error while turning Bluetooth On");
+                        sendIntent(BluetoothAdapter.STATE_OFF);
+                        transitionTo(mOffState);
+                    } else {
+                        sendMessageDelayed(ENABLE_TIMEOUT, ENABLE_TIMEOUT_DELAY);
+                    }
+                }
+                    break;
+
                 case ENABLED_READY:
+                    if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = ENABLE_READY, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
                     removeMessages(ENABLE_TIMEOUT);
-                    transitionTo(mOnState);
                     mAdapterProperties.onBluetoothReady();
-                    mAdapterService.onBluetoothEnabled();
+                    mPendingCommandState.setTurningOn(false);
+                    transitionTo(mOnState);
+                    sendIntent(BluetoothAdapter.STATE_ON);
+                    break;
+
+                case BEGIN_DISABLE: {
+                    if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = BEGIN_DISABLE" + isTurningOn + ", isTurningOff=" + isTurningOff);
+                    removeMessages(BEGIN_DISABLE); //Remove extra message we setup in USER_TURN_OFF
+                    //Log.d(TAG,"CURRENT_STATE=ON, MESSAGE = BEGIN_DISABLE_ON, requestId= " + msg.arg1);
+                    sendMessageDelayed(DISABLE_TIMEOUT, DISABLE_TIMEOUT_DELAY);
+                    boolean ret = mAdapterService.disableNative();
+                    if (!ret) {
+                        removeMessages(DISABLE_TIMEOUT);
+                        Log.e(TAG, "Error while turning Bluetooth Off");
+                        //FIXME: what about post enable services
+                        mPendingCommandState.setTurningOff(false);
+                        mPendingCommandState.setOffRequestId(-1);
+                        sendIntent(BluetoothAdapter.STATE_ON);
+                    }
+                }
                     break;
                 case DISABLED:
+                    if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = DISABLED, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
+                    removeMessages(DISABLE_TIMEOUT);
+                    sendMessageDelayed(STOP_TIMEOUT, STOP_TIMEOUT_DELAY);
+                    if (mAdapterService.stopProfileServices()) {
+                        Log.d(TAG,"Stopping profile services that were post enabled");
+                        break;
+                    }
+                    //Fall through if no post-enabled services or services already stopped
+                case STOPPED:
+                    if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = STOPPED, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
+                    removeMessages(STOP_TIMEOUT);
+                    setTurningOff(false);
+                    int requestId= getOffRequestId();
+                    setOffRequestId(-1);
+                    transitionTo(mOffState);
+                    sendIntent(BluetoothAdapter.STATE_OFF);
+                    mAdapterService.processStopped();
+                    mAdapterService.startShutdown(requestId);
+                    break;
+                case START_TIMEOUT:
+                    if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = START_TIMEOUT, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
+                    errorLog("Error enabling Bluetooth");
+                    mPendingCommandState.setTurningOn(false);
                     transitionTo(mOffState);
-                    mAdapterService.onBluetoothDisabled();
+                    sendIntent(BluetoothAdapter.STATE_OFF);
                     break;
                 case ENABLE_TIMEOUT:
+                    if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = ENABLE_TIMEOUT, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
                     errorLog("Error enabling Bluetooth");
+                    mPendingCommandState.setTurningOn(false);
                     transitionTo(mOffState);
-                    mAdapterService.onBluetoothEnableTimeout();
+                    sendIntent(BluetoothAdapter.STATE_OFF);
+                    break;
+                case STOP_TIMEOUT:
+                    if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = STOP_TIMEOUT, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
+                    errorLog("Error stopping Bluetooth profiles");
+                    mPendingCommandState.setTurningOff(false);
+                    transitionTo(mOffState);
+                    break;
+                case DISABLE_TIMEOUT:
+                    if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = DISABLE_TIMEOUT, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);
+                    errorLog("Error disabling Bluetooth");
+                    mPendingCommandState.setTurningOff(false);
+                    transitionTo(mOnState);
                     break;
                 default:
-                    Log.e(TAG, "Received unhandled event:" + msg.what);
+                    if (DBG) Log.d(TAG,"ERROR: UNEXPECTED MESSAGE: CURRENT_STATE=PENDING, MESSAGE = " + msg.what );
                     return false;
             }
             return true;
index 5ef2a57..0bc5370 100755 (executable)
@@ -48,11 +48,11 @@ final class BondStateMachine extends StateMachine {
     private StableState mStableState = new StableState();
 
     public BondStateMachine(AdapterService service, Context context,
-            AdapterProperties prop) {
+            AdapterProperties prop, RemoteDevices remoteDevices) {
         super("BondStateMachine:");
         addState(mStableState);
         addState(mPendingCommandState);
-        mRemoteDevices = RemoteDevices.getInstance(service, context);
+        mRemoteDevices = remoteDevices;
         mAdapterService = service;
         mAdapterProperties = prop;
         mContext = context;
index 5d29976..e992d5e 100644 (file)
@@ -28,13 +28,21 @@ final class JniCallbacks {
                 new JniCallbacks(remoteDevices, adapterProperties, adapterStateMachine,
                         bondStateMachine);
         } else {
-            sInstance.mRemoteDevices = remoteDevices;
-            sInstance.mAdapterProperties = adapterProperties;
-            sInstance.mAdapterStateMachine = adapterStateMachine;
-            sInstance.mBondStateMachine = bondStateMachine;
+            sInstance.init(remoteDevices, adapterProperties, adapterStateMachine,
+                    bondStateMachine);
         }
         return sInstance;
     }
+
+    void init(RemoteDevices remoteDevices,
+            AdapterProperties adapterProperties, AdapterState adapterStateMachine,
+            BondStateMachine bondStateMachine) {
+        mRemoteDevices = remoteDevices;
+        mAdapterProperties = adapterProperties;
+        mAdapterStateMachine = adapterStateMachine;
+        mBondStateMachine = bondStateMachine;
+    }
+
     public Object Clone() throws CloneNotSupportedException {
         throw new CloneNotSupportedException();
     }
index 9e86bb2..64b58d3 100644 (file)
@@ -12,6 +12,10 @@ public abstract class ProfileService extends Service {
             android.Manifest.permission.BLUETOOTH_ADMIN;
     public static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
 
+    //Profile services will not be automatically restarted.
+    //They must be explicitly restarted by AdapterService
+    private static final int PROFILE_SERVICE_MODE=Service.START_NOT_STICKY;
+
     protected BluetoothAdapter mAdapter;
     protected String mName;
 
@@ -30,10 +34,13 @@ public abstract class ProfileService extends Service {
         if (mName == null) {
             mName = "UnknownProfileService";
         }
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
 
         log("onCreate");
         super.onCreate();
-        mAdapter = BluetoothAdapter.getDefaultAdapter();
+    }
+
+    private void doStart(Intent intent) {
 
         //Start service
         if (mAdapter == null) {
@@ -41,68 +48,68 @@ public abstract class ProfileService extends Service {
         } else {
             mStartError = !start();
             if (!mStartError) {
-                notifyProfileOn();
+                notifyProfileServiceStateChange(BluetoothAdapter.STATE_ON);
             } else {
                 Log.e(mName, "Error starting profile. BluetoothAdapter is null");
             }
         }
     }
 
-    public void onStart(Intent intent, int startId) {
-        log("onStart");
-
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        log("onStartCommand()");
         if (mStartError || mAdapter == null) {
             Log.w(mName, "Stopping profile service: device does not have BT");
-            doStop();
-            return;
+            doStop(intent);
+            return PROFILE_SERVICE_MODE;
         }
 
         if (checkCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM)!=PackageManager.PERMISSION_GRANTED) {
             Log.e(mName, "Permission denied!");
-            return;
+            return PROFILE_SERVICE_MODE;
         }
 
-        String action = intent.getStringExtra(AdapterService.EXTRA_ACTION);
-        if (AdapterService.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
-            int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);        
-            if(state==BluetoothAdapter.STATE_OFF) {
-                Log.d(mName, "Received stop request...Stopping profile...");
-                doStop();
+        if (intent == null) {
+            Log.d(mName, "Restarting profile service...");
+            return PROFILE_SERVICE_MODE;
+        } else {
+            String action = intent.getStringExtra(AdapterService.EXTRA_ACTION);
+            if (AdapterService.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
+                int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+                if(state==BluetoothAdapter.STATE_OFF) {
+                    Log.d(mName, "Received stop request...Stopping profile...");
+                    doStop(intent);
+                } else if (state == BluetoothAdapter.STATE_ON) {
+                    Log.d(mName, "Received start request. Starting profile...");
+                    doStart(intent);
+                }
             }
         }
+        return PROFILE_SERVICE_MODE;
     }
 
     @Override
     public void onDestroy() {
         super.onDestroy();
-        mAdapter = null;
         log("Destroying service.");
     }
 
-    private void doStop() {
+    private void doStop(Intent intent) {
         if (stop()) {
-            notifyProfileOff();
+            notifyProfileServiceStateChange(BluetoothAdapter.STATE_OFF);
             stopSelf();
         } else {
             Log.e(mName, "Unable to stop profile");
         }
     }
 
-    protected void notifyProfileOn() {
+    protected void notifyProfileServiceStateChange(int state) {
         //Notify adapter service
         AdapterService sAdapter = AdapterService.getAdapterService();
         if (sAdapter!= null) {
-            sAdapter.onProfileServiceStateChanged(getClass().getName(), BluetoothAdapter.STATE_ON);
+            sAdapter.onProfileServiceStateChanged(getClass().getName(), state);
         }
     }
 
-    protected void notifyProfileOff() {
-        //Notify adapter service
-        AdapterService sAdapter = AdapterService.getAdapterService();
-        if (sAdapter!= null) {
-            sAdapter.onProfileServiceStateChanged(getClass().getName(), BluetoothAdapter.STATE_OFF);
-        }
-    }
 
     protected void log(String msg) {
         Log.d(mName, msg);
index df98ee0..21201f0 100755 (executable)
@@ -39,7 +39,7 @@ final class RemoteDevices {
     private HashMap<BluetoothDevice, DeviceProperties> mDevices;
     private static RemoteDevices sInstance;
 
-    private RemoteDevices(AdapterService service, Context context) {
+    RemoteDevices(AdapterService service, Context context) {
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         mContext = context;
         mAdapterService = service;
@@ -47,19 +47,19 @@ final class RemoteDevices {
         mDevices = new HashMap<BluetoothDevice, DeviceProperties>();
     }
 
-    static synchronized RemoteDevices getInstance(AdapterService service, Context context) {
-        if (sInstance == null)  {
-            sInstance = new RemoteDevices(service, context);
-        } else {
-            mContext = context;
-            mAdapterService = service;
-        }
-        return sInstance;
+    public void init() {
+        mSdpTracker.clear();
+        mDevices.clear();
     }
 
-    public void init() {
+    public void cleanup() {
         mSdpTracker.clear();
+        mSdpTracker = null;
         mDevices.clear();
+        mDevices = null;
+        mAdapterService = null;
+        mContext = null;
+        mAdapter= null;
     }
 
     public Object Clone() throws CloneNotSupportedException {
index 25ea89a..ea38a3c 100755 (executable)
@@ -53,6 +53,7 @@ public class BluetoothOppRfcommListener {
     private static final boolean V = Constants.VERBOSE;
 
     public static final int MSG_INCOMING_BTOPP_CONNECTION = 100;
+    private static final int JOIN_TIMEOUT_MS=2000;
 
     private volatile boolean mInterrupted;
     private volatile boolean mFinish;
@@ -212,7 +213,8 @@ public class BluetoothOppRfcommListener {
             try {
                 mSocketAcceptThread.interrupt();
                 if (V) Log.v(TAG, "waiting for thread to terminate");
-                mSocketAcceptThread.join();
+                mSocketAcceptThread.join(JOIN_TIMEOUT_MS);
+                if (V) Log.v(TAG, "done waiting for thread to terminate");
                 mSocketAcceptThread = null;
                 mCallback = null;
             } catch (InterruptedException e) {
index 654a47a..1dc0870 100755 (executable)
@@ -182,16 +182,16 @@ public class BluetoothOppService extends Service {
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
         if (V) Log.v(TAG, "Service onStartCommand");
-        int retCode = super.onStartCommand(intent, flags, startId);
-        if (retCode == START_STICKY) {
+        //int retCode = super.onStartCommand(intent, flags, startId);
+        //if (retCode == START_STICKY) {
             if (mAdapter == null) {
                 Log.w(TAG, "Local BT device is not enabled");
             } else {
                 startListener();
             }
             updateFromProvider();
-        }
-        return retCode;
+        //}
+        return START_NOT_STICKY;
     }
 
     private void startListener() {
@@ -212,10 +212,21 @@ public class BluetoothOppService extends Service {
 
     private static final int MSG_INCOMING_CONNECTION_RETRY = 4;
 
+    private static final int STOP_LISTENER = 200;
+
     private Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
+                case STOP_LISTENER:
+                    mSocketListener.stop();
+                    mListenStarted = false;
+                    synchronized (BluetoothOppService.this) {
+                        if (mUpdateThread == null) {
+                            stopSelf();
+                        }
+                    }
+                    break;
                 case START_LISTENER:
                     if (mAdapter.isEnabled()) {
                         startSocketListener();
@@ -383,6 +394,8 @@ public class BluetoothOppService extends Service {
                         break;
                     case BluetoothAdapter.STATE_TURNING_OFF:
                         if (V) Log.v(TAG, "Receiver DISABLED_ACTION ");
+                        //FIX: Don't block main thread
+                        /*
                         mSocketListener.stop();
                         mListenStarted = false;
                         synchronized (BluetoothOppService.this) {
@@ -390,6 +403,9 @@ public class BluetoothOppService extends Service {
                                 stopSelf();
                             }
                         }
+                        */
+                        mHandler.sendMessage(mHandler.obtainMessage(STOP_LISTENER));
+
                         break;
                 }
             }
index eacf1aa..5d7be84 100755 (executable)
@@ -183,14 +183,6 @@ public class BluetoothPbapService extends Service {
 
     public BluetoothPbapService() {
         mState = BluetoothPbap.STATE_DISCONNECTED;
-        // IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
-        //IBinder b = null;
-        //if (b == null) {
-        //    Log.e(TAG, "fail to get bluetooth service");
-            // throw new RuntimeException("Bluetooth service not available");
-        //    return;
-        //}
-       // mBluetoothService = IBluetooth.Stub.asInterface(b);
     }
 
     @Override
@@ -215,8 +207,8 @@ public class BluetoothPbapService extends Service {
 
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
-        int retCode = super.onStartCommand(intent, flags, startId);
-        if (retCode == START_STICKY) {
+        //int retCode = super.onStartCommand(intent, flags, startId);
+        //if (retCode == START_STICKY) {
             mStartId = startId;
             if (mAdapter == null) {
                 Log.w(TAG, "Stopping BluetoothPbapService: "
@@ -230,8 +222,8 @@ public class BluetoothPbapService extends Service {
                     parseIntent(intent);
                 }
             }
-        }
-        return retCode;
+        //}
+        return START_NOT_STICKY;
     }
 
     // process the intent from receiver
@@ -310,8 +302,6 @@ public class BluetoothPbapService extends Service {
             mWakeLock = null;
         }
         closeService();
-        if(mAdapter != null)
-            mAdapter = null;
         if(mSessionStatusHandler != null) {
             mSessionStatusHandler.removeCallbacksAndMessages(null);
         }