OSDN Git Service

b/2680057 Fixed a bug where bt won't connect when the phone is docked (if Settings...
authorMichael Chan <mchan@android.com>
Sat, 15 May 2010 00:41:53 +0000 (17:41 -0700)
committerMichael Chan <mchan@android.com>
Fri, 28 May 2010 00:06:50 +0000 (17:06 -0700)
The fix was to wait for the Bluetooth Headset service to come up before grabbing the settings and connecting.

Change-Id: I57affca2fe7d571c96cfeaf9ffe5439a0b02af45

src/com/android/settings/bluetooth/DockService.java
src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java

index f318987..d0f8099 100644 (file)
@@ -18,6 +18,7 @@ package com.android.settings.bluetooth;
 
 import com.android.settings.R;
 import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
+import com.android.settings.bluetooth.LocalBluetoothProfileManager.ServiceListener;
 
 import android.app.AlertDialog;
 import android.app.Notification;
@@ -48,7 +49,7 @@ import java.util.Set;
 
 public class DockService extends Service implements AlertDialog.OnMultiChoiceClickListener,
         DialogInterface.OnClickListener, DialogInterface.OnDismissListener,
-        CompoundButton.OnCheckedChangeListener {
+        CompoundButton.OnCheckedChangeListener, ServiceListener {
 
     private static final String TAG = "DockService";
 
@@ -101,6 +102,7 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
     // Created in OnCreate()
     private volatile Looper mServiceLooper;
     private volatile ServiceHandler mServiceHandler;
+    private Runnable mRunnable;
     private DockService mContext;
     private LocalBluetoothManager mBtManager;
 
@@ -138,6 +140,8 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
     @Override
     public void onDestroy() {
         if (DEBUG) Log.d(TAG, "onDestroy");
+        mRunnable = null;
+        LocalBluetoothProfileManager.removeServiceListener(this);
         if (mDialog != null) {
             mDialog.dismiss();
             mDialog = null;
@@ -228,8 +232,8 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
     // This method gets messages from both onStartCommand and mServiceHandler/mServiceLooper
     private synchronized void processMessage(Message msg) {
         int msgType = msg.what;
-        int state = msg.arg1;
-        int startId = msg.arg2;
+        final int state = msg.arg1;
+        final int startId = msg.arg2;
         boolean deferFinishCall = false;
         BluetoothDevice device = null;
         if (msg.obj != null) {
@@ -271,12 +275,23 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
                     }
 
                     mDevice = device;
-                    if (mBtManager.getDockAutoConnectSetting(device.getAddress())) {
-                        // Setting == auto connect
-                        initBtSettings(mContext, device, state, false);
-                        applyBtSettings(mDevice, startId);
+
+                    // Register first in case LocalBluetoothProfileManager
+                    // becomes ready after isManagerReady is called and it
+                    // would be too late to register a service listener.
+                    LocalBluetoothProfileManager.addServiceListener(this);
+                    if (LocalBluetoothProfileManager.isManagerReady()) {
+                        handleDocked(device, state, startId);
+                        // Not needed after all
+                        LocalBluetoothProfileManager.removeServiceListener(this);
                     } else {
-                        createDialog(mContext, mDevice, state, startId);
+                        final BluetoothDevice d = device;
+                        mRunnable = new Runnable() {
+                            public void run() {
+                                handleDocked(d, state, startId);
+                            }
+                        };
+                        deferFinishCall = true;
                     }
                 }
                 break;
@@ -721,8 +736,21 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
         }
     }
 
+    private synchronized void handleDocked(final BluetoothDevice device, final int state,
+            final int startId) {
+        if (mBtManager.getDockAutoConnectSetting(device.getAddress())) {
+            // Setting == auto connect
+            initBtSettings(mContext, device, state, false);
+            applyBtSettings(mDevice, startId);
+        } else {
+            createDialog(mContext, device, state, startId);
+        }
+    }
+
     private synchronized void handleUndocked(Context context, LocalBluetoothManager localManager,
             BluetoothDevice device) {
+        mRunnable = null;
+        LocalBluetoothProfileManager.removeServiceListener(this);
         if (mDialog != null) {
             mDialog.dismiss();
             mDialog = null;
@@ -778,4 +806,15 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
         editor.commit();
         return;
     }
+
+    public synchronized void onServiceConnected() {
+        if (mRunnable != null) {
+            mRunnable.run();
+            mRunnable = null;
+            LocalBluetoothProfileManager.removeServiceListener(this);
+        }
+    }
+
+    public void onServiceDisconnected() {
+    }
 }
index f3aaade..6179dc7 100644 (file)
@@ -28,6 +28,8 @@ import android.util.Log;
 
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -53,6 +55,29 @@ public abstract class LocalBluetoothProfileManager {
         BluetoothUuid.ObexObjectPush
     };
 
+    /**
+     * An interface for notifying BluetoothHeadset IPC clients when they have
+     * been connected to the BluetoothHeadset service.
+     */
+    public interface ServiceListener {
+        /**
+         * Called to notify the client when this proxy object has been
+         * connected to the BluetoothHeadset service. Clients must wait for
+         * this callback before making IPC calls on the BluetoothHeadset
+         * service.
+         */
+        public void onServiceConnected();
+
+        /**
+         * Called to notify the client that this proxy object has been
+         * disconnected from the BluetoothHeadset service. Clients must not
+         * make IPC calls on the BluetoothHeadset service after this callback.
+         * This callback will currently only occur if the application hosting
+         * the BluetoothHeadset service, but may be called more often in future.
+         */
+        public void onServiceDisconnected();
+    }
+
     // TODO: close profiles when we're shutting down
     private static Map<Profile, LocalBluetoothProfileManager> sProfileMap =
             new HashMap<Profile, LocalBluetoothProfileManager>();
@@ -76,6 +101,26 @@ public abstract class LocalBluetoothProfileManager {
         }
     }
 
+    private static LinkedList<ServiceListener> mServiceListeners = new LinkedList<ServiceListener>();
+
+    public static void addServiceListener(ServiceListener l) {
+        mServiceListeners.add(l);
+    }
+
+    public static void removeServiceListener(ServiceListener l) {
+        mServiceListeners.remove(l);
+    }
+
+    public static boolean isManagerReady() {
+        // Getting just the headset profile is fine for now. Will need to deal with A2DP
+        // and others if they aren't always in a ready state.
+        LocalBluetoothProfileManager profileManager = sProfileMap.get(Profile.HEADSET);
+        if (profileManager == null) {
+            return sProfileMap.size() > 0;
+        }
+        return profileManager.isProfileReady();
+    }
+
     public static LocalBluetoothProfileManager getProfileManager(LocalBluetoothManager localManager,
             Profile profile) {
         // Note: This code assumes that "localManager" is same as the
@@ -144,6 +189,8 @@ public abstract class LocalBluetoothProfileManager {
         return SettingsBtStatus.isConnectionStatusConnected(getConnectionStatus(device));
     }
 
+    public abstract boolean isProfileReady();
+
     // TODO: int instead of enum
     public enum Profile {
         HEADSET(R.string.bluetooth_profile_headset),
@@ -247,6 +294,11 @@ public abstract class LocalBluetoothProfileManager {
                 return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
             }
         }
+
+        @Override
+        public boolean isProfileReady() {
+            return true;
+        }
     }
 
     /**
@@ -256,6 +308,7 @@ public abstract class LocalBluetoothProfileManager {
             implements BluetoothHeadset.ServiceListener {
         private BluetoothHeadset mService;
         private Handler mUiHandler = new Handler();
+        private boolean profileReady = false;
 
         public HeadsetProfileManager(LocalBluetoothManager localManager) {
             super(localManager);
@@ -263,6 +316,7 @@ public abstract class LocalBluetoothProfileManager {
         }
 
         public void onServiceConnected() {
+            profileReady = true;
             // This could be called on a non-UI thread, funnel to UI thread.
             mUiHandler.post(new Runnable() {
                 public void run() {
@@ -277,9 +331,28 @@ public abstract class LocalBluetoothProfileManager {
                                                    BluetoothHeadset.STATE_CONNECTED);
                 }
             });
+
+            if (mServiceListeners.size() > 0) {
+                Iterator<ServiceListener> it = mServiceListeners.iterator();
+                while(it.hasNext()) {
+                    it.next().onServiceConnected();
+                }
+            }
         }
 
         public void onServiceDisconnected() {
+            profileReady = false;
+            if (mServiceListeners.size() > 0) {
+                Iterator<ServiceListener> it = mServiceListeners.iterator();
+                while(it.hasNext()) {
+                    it.next().onServiceDisconnected();
+                }
+            }
+        }
+
+        @Override
+        public boolean isProfileReady() {
+            return profileReady;
         }
 
         @Override
@@ -424,6 +497,11 @@ public abstract class LocalBluetoothProfileManager {
         }
 
         @Override
+        public boolean isProfileReady() {
+            return true;
+        }
+
+        @Override
         public int convertState(int oppState) {
             switch (oppState) {
             case 0: