OSDN Git Service

b/2498180 b/2568119 Retry if BT dock disconnects unexpectedly
authorMichael Chan <mchan@android.com>
Tue, 6 Apr 2010 21:49:30 +0000 (14:49 -0700)
committerMichael Chan <mchan@android.com>
Tue, 6 Apr 2010 22:42:04 +0000 (15:42 -0700)
Change-Id: Ic4771e7c305192fee56f5f80c6cabeecf11a99dc

AndroidManifest.xml
src/com/android/settings/bluetooth/DockEventReceiver.java
src/com/android/settings/bluetooth/DockService.java

index 97fbaec..3499a42 100644 (file)
                 <category android:name="android.intent.category.VOICE_LAUNCH" />
             </intent-filter>
         </activity>
-        
+
         <activity android:name="VoiceInputOutputSettings"
                 android:label="@string/voice_input_output_settings">
             <intent-filter>
             <intent-filter>
                 <action android:name="android.intent.action.DOCK_EVENT" />
                 <action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
+                <action android:name="android.bluetooth.headset.action.STATE_CHANGED" />
+                <action android:name="android.bluetooth.a2dp.action.SINK_STATE_CHANGED" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </receiver>
index 2d634b2..6d11972 100644 (file)
 
 package com.android.settings.bluetooth;
 
+import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
+
 import android.app.Service;
+import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.PowerManager;
 import android.util.Log;
 
@@ -75,6 +80,54 @@ public class DockEventReceiver extends BroadcastReceiver {
                     if (DEBUG) Log.e(TAG, "Unknown state");
                     break;
             }
+        } else if (BluetoothHeadset.ACTION_STATE_CHANGED.equals(intent.getAction())) {
+            /*
+             *  Reconnect to the dock if:
+             *  1) it is a dock
+             *  2) it is disconnected
+             *  3) the disconnect is initiated remotely
+             *  4) the dock is still docked (check can only be done in the Service)
+             */
+            if (device == null) {
+                if (DEBUG) Log.d(TAG, "Device is missing");
+                return;
+            }
+
+            int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
+                    BluetoothHeadset.STATE_CONNECTED);
+            if (newState != BluetoothHeadset.STATE_DISCONNECTED) return;
+
+            int source = intent.getIntExtra(BluetoothHeadset.EXTRA_DISCONNECT_INITIATOR,
+                    BluetoothHeadset.LOCAL_DISCONNECT);
+            if (source != BluetoothHeadset.REMOTE_DISCONNECT) return;
+
+            // Too bad, the dock state can't be checked from a BroadcastReceiver.
+            Intent i = new Intent(intent);
+            i.setClass(context, DockService.class);
+            beginStartingService(context, i);
+
+        } else if (BluetoothA2dp.ACTION_SINK_STATE_CHANGED.equals(intent.getAction())) {
+            /*
+             *  Reconnect to the dock if:
+             *  1) it is a dock
+             *  2) it is an unexpected disconnect i.e. didn't go through disconnecting state
+             *  3) the dock is still docked (check can only be done in the Service)
+             */
+            if (device == null) {
+                if (DEBUG) Log.d(TAG, "Device is missing");
+                return;
+            }
+
+            int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0);
+            int oldState = intent.getIntExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, 0);
+            if (newState == BluetoothA2dp.STATE_DISCONNECTED &&
+                    oldState != BluetoothA2dp.STATE_DISCONNECTING) {
+                // Too bad, the dock state can't be checked from a BroadcastReceiver.
+                Intent i = new Intent(intent);
+                i.setClass(context, DockService.class);
+                beginStartingService(context, i);
+            }
+
         } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
             int btState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
             if (btState != BluetoothAdapter.STATE_TURNING_ON) {
index 1425e23..f318987 100644 (file)
@@ -87,6 +87,15 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
     private static final String SHARED_PREFERENCES_KEY_DISABLE_BT =
         "disable_bt";
 
+    private static final String SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT =
+        "connect_retry_count";
+
+    /*
+     * If disconnected unexpectedly, reconnect up to 6 times. Each profile counts
+     * as one time so it's only 3 times for both profiles on the car dock.
+     */
+    private static final int MAX_CONNECT_RETRY = 6;
+
     private static final int INVALID_STARTID = -100;
 
     // Created in OnCreate()
@@ -161,6 +170,32 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
             return START_NOT_STICKY;
         }
 
+        /*
+         * This assumes that the intent sender has checked that this is a dock
+         * and that the intent is for a disconnect
+         */
+        if (BluetoothHeadset.ACTION_STATE_CHANGED.equals(intent.getAction())) {
+            BluetoothDevice disconnectedDevice = intent
+                    .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+            int retryCount = getSettingInt(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT, 0);
+            if (retryCount < MAX_CONNECT_RETRY) {
+                setSettingInt(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT, retryCount + 1);
+                handleUnexpectedDisconnect(disconnectedDevice, Profile.HEADSET, startId);
+            }
+            return START_NOT_STICKY;
+        } else if (BluetoothA2dp.ACTION_SINK_STATE_CHANGED.equals(intent.getAction())) {
+            BluetoothDevice disconnectedDevice = intent
+                    .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+            int retryCount = getSettingInt(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT, 0);
+            if (retryCount < MAX_CONNECT_RETRY) {
+                setSettingInt(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT, retryCount + 1);
+                handleUnexpectedDisconnect(disconnectedDevice, Profile.A2DP, startId);
+            }
+            return START_NOT_STICKY;
+        }
+
         Message msg = parseIntent(intent);
         if (msg == null) {
             // Bad intent
@@ -169,6 +204,10 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
             return START_NOT_STICKY;
         }
 
+        if (msg.what == MSG_TYPE_DOCKED) {
+            removeSetting(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT);
+        }
+
         msg.arg2 = startId;
         processMessage(msg);
 
@@ -248,10 +287,10 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
 
                 if (DEBUG) {
                     Log.d(TAG, "DISABLE_BT_WHEN_UNDOCKED = "
-                            + getSetting(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED));
+                            + getSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED));
                 }
 
-                if (getSetting(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED)) {
+                if (getSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED)) {
                     // BT was disabled when we first docked
                     if (!hasOtherConnectedDevices(device)) {
                         if(DEBUG) Log.d(TAG, "QUEUED BT DISABLE");
@@ -280,7 +319,7 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
                     removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED);
                 } else {
                     // disable() returned an error. Persist a flag to disable BT later
-                    setSetting(SHARED_PREFERENCES_KEY_DISABLE_BT, true);
+                    setSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT, true);
                     mPendingTurnOffStartId = startId;
                     deferFinishCall = true;
                     if(DEBUG) Log.d(TAG, "disable failed. try again later " + startId);
@@ -509,7 +548,7 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
                 } else {
                     if (DEBUG) {
                         Log.d(TAG, "A DISABLE_BT_WHEN_UNDOCKED = "
-                                + getSetting(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED));
+                                + getSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED));
                     }
                     // Reconnect if docked and bluetooth was enabled by user.
                     Intent i = registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
@@ -522,7 +561,7 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
                             if (device != null) {
                                 connectIfEnabled(device);
                             }
-                        } else if (getSetting(SHARED_PREFERENCES_KEY_DISABLE_BT)
+                        } else if (getSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT)
                                 && mBtManager.getBluetoothAdapter().disable()) {
                             mPendingTurnOffStartId = startId;
                             removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT);
@@ -565,6 +604,34 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
         }
     }
 
+    private void handleUnexpectedDisconnect(BluetoothDevice disconnectedDevice, Profile profile,
+            int startId) {
+        synchronized (this) {
+            if (DEBUG) Log.d(TAG, "handling failed connect for " + disconnectedDevice);
+
+            // Reconnect if docked.
+            if (disconnectedDevice != null) {
+                // registerReceiver can't be called from a BroadcastReceiver
+                Intent i = registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
+                if (i != null) {
+                    int state = i.getIntExtra(Intent.EXTRA_DOCK_STATE,
+                            Intent.EXTRA_DOCK_STATE_UNDOCKED);
+                    if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
+                        BluetoothDevice dockedDevice = i
+                                .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                        if (dockedDevice != null && dockedDevice.equals(disconnectedDevice)) {
+                            CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(mContext,
+                                    mBtManager, dockedDevice);
+                            cachedDevice.connect(profile);
+                        }
+                    }
+                }
+            }
+
+            DockEventReceiver.finishStartingService(this, startId);
+        }
+    }
+
     private synchronized void connectIfEnabled(BluetoothDevice device) {
         CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(mContext, mBtManager, device);
         List<Profile> profiles = cachedDevice.getConnectableProfiles();
@@ -612,7 +679,8 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
                             mPendingDevice = device;
                             mPendingStartId = startId;
                             if (btState != BluetoothAdapter.STATE_TURNING_ON) {
-                                setSetting(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED, true);
+                                setSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED,
+                                        true);
                             }
                             return;
                     }
@@ -676,16 +744,29 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
         return cachedBluetoothDevice;
     }
 
-    private boolean getSetting(String key) {
+    private boolean getSettingBool(String key) {
         SharedPreferences sharedPref = getSharedPreferences(SHARED_PREFERENCES_NAME,
                 Context.MODE_PRIVATE);
         return sharedPref.getBoolean(key, false);
     }
 
-    private void setSetting(String key, boolean disableBt) {
+    private int getSettingInt(String key, int defaultValue) {
+        SharedPreferences sharedPref = getSharedPreferences(SHARED_PREFERENCES_NAME,
+                Context.MODE_PRIVATE);
+        return sharedPref.getInt(key, defaultValue);
+    }
+
+    private void setSettingBool(String key, boolean bool) {
+        SharedPreferences.Editor editor = getSharedPreferences(SHARED_PREFERENCES_NAME,
+                Context.MODE_PRIVATE).edit();
+        editor.putBoolean(key, bool);
+        editor.commit();
+    }
+
+    private void setSettingInt(String key, int value) {
         SharedPreferences.Editor editor = getSharedPreferences(SHARED_PREFERENCES_NAME,
                 Context.MODE_PRIVATE).edit();
-        editor.putBoolean(key, disableBt);
+        editor.putInt(key, value);
         editor.commit();
     }