OSDN Git Service

bluetooth tethering
authorDanica Chang <danicachang@google.com>
Wed, 11 Aug 2010 01:41:29 +0000 (18:41 -0700)
committerDanica Chang <danicachang@google.com>
Wed, 18 Aug 2010 02:07:07 +0000 (19:07 -0700)
Change-Id: I8dfb1c85bb8b963d2937b8bc4a4c9f0cf641785d

res/values/strings.xml
res/xml/tether_prefs.xml
res/xml/wireless_settings.xml
src/com/android/settings/TetherSettings.java
src/com/android/settings/WirelessSettings.java
src/com/android/settings/bluetooth/BluetoothDevicePreference.java
src/com/android/settings/bluetooth/BluetoothEventRedirector.java
src/com/android/settings/bluetooth/BluetoothSettings.java
src/com/android/settings/bluetooth/CachedBluetoothDevice.java
src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java
src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java

index 241022e..5283414 100644 (file)
     <string name="bluetooth_profile_opp">Transfer</string>
     <!-- Bluetooth settings. The user-visible string that is used whenever referring to the HID profile. -->
     <string name="bluetooth_profile_hid">Input Device</string>
+    <!-- Bluetooth settings. The user-visible string that is used whenever referring to the PAN profile. [CHAR LIMIT=25]-->
+    <string name="bluetooth_profile_pan">Tethering</string>
 
     <!-- Bluetooth settings.  The summary string when a device is connected to the A2DP profile. -->
     <string name="bluetooth_summary_connected_to_a2dp">Connected to media audio</string>
     <string name="bluetooth_summary_connected_to_a2dp_headset">Connected to phone and media audio</string>
     <!-- Bluetooth settings. The summary string when a device is connected to the HID profile. -->
     <string name="bluetooth_summary_connected_to_hid">Connected to input device</string>
+    <!-- Bluetooth settings. The summary string when a device is connected to the PAN profile. [CHAR LIMIT=35]-->
+    <string name="bluetooth_summary_connected_to_pan">Tethered</string>
 
     <!-- Bluetooth settings.  Connection options screen.  The title of the screen. -->
     <string name="bluetooth_device_advanced_title"><xliff:g id="device_name">%1$s</xliff:g> options</string>
     <string name="bluetooth_opp_profile_summary_not_connected">Not connected to file transfer server</string>
     <!-- Bluetooth settings. Connection options screen. The summary for the HID checkbox preference when HID is connected. -->
     <string name="bluetooth_hid_profile_summary_connected">Connected to input device</string>
+    <!-- Bluetooth settings. Connection options screen. The summary for the PAN checkbox preference when PAN is connected. [CHAR LIMIT=25]-->
+    <string name="bluetooth_pan_profile_summary_connected">Tethered</string>
 
     <!-- Bluetooth settings.  Connection options screen.  The summary for the A2DP checkbox preference that describes how checking it will set the A2DP profile as preferred. -->
     <string name="bluetooth_a2dp_profile_summary_use_for">Use for media audio</string>
     <string name="call_settings_summary">Set up voicemail, call forwarding, call waiting, caller ID</string>
 
     <!-- Tethering controls, item title to go into the tethering settings -->
-    <string name="tether_settings_title_usb">Tethering</string>
+    <!-- Tethering controls, item title to go into the tethering settings when only USB tethering is available [CHAR LIMIT=25]-->
+    <string name="tether_settings_title_usb">USB tethering</string>
+    <!-- Tethering controls, item title to go into the tethering settings when only Wifi tethering is available [CHAR LIMIT=25]-->
     <string name="tether_settings_title_wifi">Portable hotspot</string>
-    <string name="tether_settings_title_both">Tethering &amp; portable hotspot</string>
+    <!-- Tethering controls, item title to go into the tethering settings when only Bluetooth tethering is available [CHAR LIMIT=25]-->
+    <string name="tether_settings_title_bluetooth">Bluetooth tethering</string>
+    <!-- Tethering controls, item title to go into the tethering settings when USB and Bluetooth tethering are available [CHAR LIMIT=25]-->
+    <string name="tether_settings_title_usb_bluetooth">Tethering</string>
+    <!-- Tethering controls, item title to go into the tethering settings when USB, Bluetooth and Wifi tethering are available [CHAR LIMIT=25]-->
+    <string name="tether_settings_title_all">Tethering &amp; portable hotspot</string>
+
     <!-- Tethering controls, the item summary for the user to go into the tethering settings -->
+    <!-- Tethering controls, the item summary for the user to go into the tethering settings when only USB tethering is available [CHAR LIMIT=100]-->
     <string name="tether_settings_summary_usb">Share your phone\'s mobile data connection via USB</string>
+    <!-- Tethering controls, the item summary for the user to go into the tethering settings when only Wifi tethering is available [CHAR LIMIT=100]-->
     <string name="tether_settings_summary_wifi">Share your phone\'s mobile connection as a portable Wi-Fi hotspot</string>
-    <string name="tether_settings_summary_both">Share your phone\'s mobile data connection via USB or as a portable Wi-Fi hotspot</string>
+    <!-- Tethering controls, the item summary for the user to go into the tethering settings when only Bluetooth tethering is available [CHAR LIMIT=100]-->
+    <string name="tether_settings_summary_bluetooth">Share your phone\'s mobile connection via Bluetooth</string>
+    <!-- Tethering controls, the item summary for the user to go into the tethering settings when USB and Wifi tethering are available [CHAR LIMIT=100]-->
+    <string name="tether_settings_summary_usb_wifi">Share your phone\'s mobile data connection via USB or as a portable Wi-Fi hotspot</string>
+    <!-- Tethering controls, the item summary for the user to go into the tethering settings when USB and Bluetooth tethering are available [CHAR LIMIT=100]-->
+    <string name="tether_settings_summary_usb_bluetooth">Share your phone\'s mobile data connection via USB or Bluetooth</string>
+    <!-- Tethering controls, the item summary for the user to go into the tethering settings when Bluetooth and Wifi tethering are available [CHAR LIMIT=100]-->
+    <string name="tether_settings_summary_wifi_bluetooth">Share your phone\'s mobile data connection via Bluetooth or as a portable Wi-Fi hotspot</string>
+    <!-- Tethering controls, the item summary for the user to go into the tethering settings when USB, Wifi, and Bluetooth tethering are available [CHAR LIMIT=100]-->
+    <string name="tether_settings_summary_all">Share your phone\'s mobile data connection via USB, Wi-Fi, or Bluetooth</string>
 
     <!-- USB Tethering options -->
     <string name="usb_title">USB</string>
     <!-- USB errored subtext - shown when USB is broken for some reason -->
     <string name="usb_tethering_errored_subtext">USB tethering error</string>
 
-    <!-- Thethering help button - calls up a web view with general tethering info -->
+    <!-- Bluetooth Tethering settings-->
+    <!-- Label for bluetooth tether checkbox [CHAR LIMIT=25]-->
+    <string name="bluetooth_tether_checkbox_text">Bluetooth tethering</string>
+    <!-- Bluetooth available subtext - shown when Bluetooth tethering is turned on but it is not currently tethered to any devices [CHAR LIMIT=50]-->
+    <string name="bluetooth_tethering_available_subtext">Bluetooth tethering on, but not connected</string>
+    <!-- Bluetooth connected subtext - shown when a device is tethered over Bluetooth [CHAR LIMIT=50]-->
+    <string name="bluetooth_tethering_connected_subtext">Bluetooth tethering on and connected</string>
+    <!-- Bluetooth tethering off subtext - shown when Bluetooth Tethering is turned off [CHAR LIMIT=50]-->
+    <string name="bluetooth_tethering_off_subtext">Bluetooth tethering off</string>
+    <!-- Bluetooth errored subtext - shown when Bluetooth is broken for some reason [CHAR LIMIT=50]-->
+    <string name="bluetooth_tethering_errored_subtext">Bluetooth tethering error</string>
+    <!-- Bluetooth Tethering settings. Error message shown when trying to connect an 8th device [CHAR LIMIT=50]-->
+    <string name="bluetooth_tethering_overflow_error">Cannot tether to more than <xliff:g id="maxConnection">%1$d</xliff:g> devices</string>
+    <!-- Bluetooth Tethering settings.  Message for untethering from a bluetooth device [CHAR LIMIT=50]-->
+    <string name="bluetooth_untether_blank"><xliff:g id="device_name">%1$s</xliff:g> will be untethered.</string>
+
+    <string name="bluetooth_tether_settings_text">Bluetooth tethering settings</string>
+    <string name="bluetooth_tether_settings_subtext">Select Bluetooth devices to tether</string>
+
+    <!-- Tethering help button - calls up a web view with general tethering info -->
     <string name="tethering_help_button_text">Help</string>
 
     <!-- Wireless controls, item title to go into the network settings -->
index 77fc7c6..95dfaf8 100644 (file)
             android:targetClass="com.android.settings.wifi.WifiApSettings" />
     </PreferenceScreen>
 
+    <CheckBoxPreference
+        android:key="enable_bluetooth_tethering"
+        android:title="@string/bluetooth_tether_checkbox_text"
+        android:persistent="false" />
+
+    <PreferenceScreen
+        android:key="bluetooth_tether_settings"
+        android:title="@string/bluetooth_tether_settings_text"
+        android:summary="@string/bluetooth_tether_settings_subtext" >
+        <intent
+            android:action="com.android.settings.bluetooth.action.LAUNCH_TETHER_PICKER"
+            android:targetPackage="com.android.settings"
+            android:targetClass="com.android.settings.bluetooth.BluetoothSettings" />
+    </PreferenceScreen>
+
     <PreferenceScreen
         android:key="tethering_help"
         android:title="@string/tethering_help_button_text" >
index 13b8344..8573ed0 100644 (file)
@@ -58,8 +58,8 @@
 
     <PreferenceScreen
         android:key="tether_settings"
-        android:title="@string/tether_settings_title_both"
-        android:summary="@string/tether_settings_summary_both">
+        android:title="@string/tether_settings_title_all"
+        android:summary="@string/tether_settings_summary_all">
         <intent
             android:action="android.intent.action.MAIN"
             android:targetPackage="com.android.settings"
index 22377ab..67049ff 100644 (file)
@@ -20,7 +20,13 @@ import com.android.settings.wifi.WifiApEnabler;
 
 import android.app.AlertDialog;
 import android.app.Dialog;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothPan;
+import android.bluetooth.IBluetooth;
 import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -32,6 +38,7 @@ import android.preference.CheckBoxPreference;
 import android.preference.Preference;
 import android.preference.PreferenceActivity;
 import android.preference.PreferenceScreen;
+import android.util.Log;
 import android.webkit.WebView;
 
 import java.io.InputStream;
@@ -42,9 +49,13 @@ import java.util.Locale;
  * Displays preferences for Tethering.
  */
 public class TetherSettings extends PreferenceActivity {
+    private static final String TAG = "TetheringSettings";
+
     private static final String USB_TETHER_SETTINGS = "usb_tether_settings";
     private static final String ENABLE_WIFI_AP = "enable_wifi_ap";
     private static final String WIFI_AP_SETTINGS = "wifi_ap_settings";
+    private static final String ENABLE_BLUETOOTH_TETHERING = "enable_bluetooth_tethering";
+    private static final String BLUETOOTH_TETHER_SETTINGS = "bluetooth_tether_settings";
     private static final String TETHERING_HELP = "tethering_help";
     private static final String USB_HELP_MODIFIER = "usb_";
     private static final String WIFI_HELP_MODIFIER = "wifi_";
@@ -59,6 +70,10 @@ public class TetherSettings extends PreferenceActivity {
     private CheckBoxPreference mEnableWifiAp;
     private PreferenceScreen mWifiApSettings;
     private WifiApEnabler mWifiApEnabler;
+
+    private CheckBoxPreference mBluetoothTether;
+    private PreferenceScreen mBluetoothSettings;
+
     private PreferenceScreen mTetherHelp;
 
     private BroadcastReceiver mTetherChangeReceiver;
@@ -67,36 +82,68 @@ public class TetherSettings extends PreferenceActivity {
 
     private String[] mWifiRegexs;
 
+    private String[] mBluetoothRegexs;
+    private BluetoothPan mBluetoothPan;
+
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
+        mBluetoothPan = new BluetoothPan(this);
         addPreferencesFromResource(R.xml.tether_prefs);
 
         mEnableWifiAp = (CheckBoxPreference) findPreference(ENABLE_WIFI_AP);
         mWifiApSettings = (PreferenceScreen) findPreference(WIFI_AP_SETTINGS);
         mUsbTether = (CheckBoxPreference) findPreference(USB_TETHER_SETTINGS);
+        mBluetoothTether = (CheckBoxPreference) findPreference(ENABLE_BLUETOOTH_TETHERING);
+        mBluetoothSettings = (PreferenceScreen) findPreference(BLUETOOTH_TETHER_SETTINGS);
         mTetherHelp = (PreferenceScreen) findPreference(TETHERING_HELP);
 
         ConnectivityManager cm =
                 (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
 
         mUsbRegexs = cm.getTetherableUsbRegexs();
-        if (mUsbRegexs.length == 0 || Utils.isMonkeyRunning()) {
-            getPreferenceScreen().removePreference(mUsbTether);
+        mWifiRegexs = cm.getTetherableWifiRegexs();
+        mBluetoothRegexs = cm.getTetherableBluetoothRegexs();
 
-            setTitle(R.string.tether_settings_title_wifi);
-        }
+        boolean usbAvailable = mUsbRegexs.length != 0;
+        boolean wifiAvailable = mWifiRegexs.length != 0;
+        boolean bluetoothAvailable = mBluetoothRegexs.length != 0;
 
-        mWifiRegexs = cm.getTetherableWifiRegexs();
-        if (mWifiRegexs.length == 0) {
+
+        if (!usbAvailable || Utils.isMonkeyRunning()) {
+            getPreferenceScreen().removePreference(mUsbTether);
+        }
+        if (!wifiAvailable) {
             getPreferenceScreen().removePreference(mEnableWifiAp);
             getPreferenceScreen().removePreference(mWifiApSettings);
-
+        }
+        if (!bluetoothAvailable) {
+            getPreferenceScreen().removePreference(mBluetoothTether);
+            getPreferenceScreen().removePreference(mBluetoothSettings);
+        } else {
+            if (mBluetoothPan.isTetheringOn()) {
+                mBluetoothTether.setChecked(true);
+                mBluetoothSettings.setEnabled(true);
+            } else {
+                mBluetoothTether.setChecked(false);
+                mBluetoothSettings.setEnabled(false);
+            }
+        }
+        if (wifiAvailable && usbAvailable && bluetoothAvailable){
+            setTitle(R.string.tether_settings_title_all);
+        } else if (wifiAvailable && usbAvailable){
+            setTitle(R.string.tether_settings_title_all);
+        } else if (wifiAvailable && bluetoothAvailable){
+            setTitle(R.string.tether_settings_title_all);
+        } else if (wifiAvailable) {
+            setTitle(R.string.tether_settings_title_wifi);
+        } else if (usbAvailable && bluetoothAvailable) {
+            setTitle(R.string.tether_settings_title_usb_bluetooth);
+        } else if (usbAvailable) {
             setTitle(R.string.tether_settings_title_usb);
-        } else if (mUsbRegexs.length != 0) {
-            // have both
-            setTitle(R.string.tether_settings_title_both);
+        } else {
+            setTitle(R.string.tether_settings_title_bluetooth);
         }
         mWifiApEnabler = new WifiApEnabler(this, mEnableWifiAp);
         mView = new WebView(this);
@@ -148,6 +195,7 @@ public class TetherSettings extends PreferenceActivity {
     }
 
     private class TetherChangeReceiver extends BroadcastReceiver {
+        @Override
         public void onReceive(Context content, Intent intent) {
             if (intent.getAction().equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)) {
                 // TODO - this should understand the interface types
@@ -157,11 +205,14 @@ public class TetherSettings extends PreferenceActivity {
                         ConnectivityManager.EXTRA_ACTIVE_TETHER);
                 ArrayList<String> errored = intent.getStringArrayListExtra(
                         ConnectivityManager.EXTRA_ERRORED_TETHER);
-                updateState((String[]) available.toArray(), (String[]) active.toArray(),
-                        (String[]) errored.toArray());
+                updateState(available.toArray(new String[available.size()]),
+                        active.toArray(new String[active.size()]),
+                        errored.toArray(new String[errored.size()]));
             } else if (intent.getAction().equals(Intent.ACTION_MEDIA_SHARED) ||
                        intent.getAction().equals(Intent.ACTION_MEDIA_UNSHARED)) {
                 updateState();
+            } else if (intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+                updateState();
             }
         }
     }
@@ -170,8 +221,8 @@ public class TetherSettings extends PreferenceActivity {
     protected void onResume() {
         super.onResume();
 
-        IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
         mTetherChangeReceiver = new TetherChangeReceiver();
+        IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
         Intent intent = registerReceiver(mTetherChangeReceiver, filter);
 
         filter = new IntentFilter();
@@ -180,6 +231,10 @@ public class TetherSettings extends PreferenceActivity {
         filter.addDataScheme("file");
         registerReceiver(mTetherChangeReceiver, filter);
 
+        filter = new IntentFilter();
+        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        registerReceiver(mTetherChangeReceiver, filter);
+
         if (intent != null) mTetherChangeReceiver.onReceive(this, intent);
         mWifiApEnabler.resume();
     }
@@ -204,6 +259,13 @@ public class TetherSettings extends PreferenceActivity {
 
     private void updateState(String[] available, String[] tethered,
             String[] errored) {
+        updateUsbState(available, tethered, errored);
+        updateBluetoothState(available, tethered, errored);
+    }
+
+
+    private void updateUsbState(String[] available, String[] tethered,
+            String[] errored) {
         ConnectivityManager cm =
                 (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
         boolean usbTethered = false;
@@ -260,6 +322,66 @@ public class TetherSettings extends PreferenceActivity {
         }
     }
 
+    private void updateBluetoothState(String[] available, String[] tethered,
+            String[] errored) {
+        ConnectivityManager cm =
+                (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
+        boolean bluetoothTethered = false;
+        boolean bluetoothAvailable = false;
+        int bluetoothError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
+        boolean bluetoothErrored = false;
+        for (String s : available) {
+            for (String regex : mBluetoothRegexs) {
+                if (s.matches(regex)) {
+                    bluetoothAvailable = true;
+                    if (bluetoothError == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                        bluetoothError = cm.getLastTetherError(s);
+                    }
+                }
+            }
+        }
+        for (String s : tethered) {
+            for (String regex : mBluetoothRegexs) {
+                if (s.matches(regex)) bluetoothTethered = true;
+            }
+        }
+        for (String s: errored) {
+            for (String regex : mBluetoothRegexs) {
+                if (s.matches(regex)) bluetoothErrored = true;
+            }
+        }
+
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        int btState = adapter.getState();
+        if (btState == BluetoothAdapter.STATE_TURNING_OFF) {
+            mBluetoothTether.setEnabled(false);
+            mBluetoothSettings.setEnabled(false);
+            mBluetoothTether.setSummary(R.string.wifi_stopping);
+        } else if (btState == BluetoothAdapter.STATE_TURNING_ON) {
+            mBluetoothTether.setEnabled(false);
+            mBluetoothSettings.setEnabled(false);
+            mBluetoothTether.setSummary(R.string.bluetooth_turning_on);
+        } else if (mBluetoothPan.isTetheringOn()) {
+            mBluetoothTether.setChecked(true);
+            if (btState == BluetoothAdapter.STATE_ON) {
+                mBluetoothTether.setEnabled(true);
+                mBluetoothSettings.setEnabled(true);
+                if (bluetoothTethered) {
+                    mBluetoothTether.setSummary(R.string.bluetooth_tethering_connected_subtext);
+                } else if (bluetoothErrored) {
+                    mBluetoothTether.setSummary(R.string.bluetooth_tethering_errored_subtext);
+                } else {
+                    mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext);
+                }
+            }
+        } else {
+            mBluetoothTether.setEnabled(true);
+            mBluetoothTether.setChecked(false);
+            mBluetoothSettings.setEnabled(false);
+            mBluetoothTether.setSummary(R.string.bluetooth_tethering_off_subtext);
+        }
+    }
+
     @Override
     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
         if (preference == mUsbTether) {
@@ -296,8 +418,47 @@ public class TetherSettings extends PreferenceActivity {
                 }
                 mUsbTether.setSummary("");
             }
-        } else if (preference == mTetherHelp) {
+        } else if(preference == mBluetoothTether) {
+            boolean bluetoothTetherState = mBluetoothTether.isChecked();
+
+            if (bluetoothTetherState) {
+                // turn on Bluetooth first
+                BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+                if (adapter.getState() == BluetoothAdapter.STATE_OFF) {
+                    adapter.enable();
+                    mBluetoothTether.setSummary(R.string.bluetooth_turning_on);
+                    mBluetoothTether.setEnabled(false);
+                    mBluetoothSettings.setEnabled(false);
+                } else {
+                    mBluetoothSettings.setEnabled(true);
+                }
+
+                mBluetoothPan.setBluetoothTethering(true,
+                        BluetoothPan.NAP_ROLE, BluetoothPan.NAP_BRIDGE);
+                mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext);
+            } else {
+                boolean errored = false;
 
+                ConnectivityManager cm =
+                    (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
+                String [] tethered = cm.getTetheredIfaces();
+                String bluetoothIface = findIface(tethered, mBluetoothRegexs);
+                if (bluetoothIface != null &&
+                        cm.untether(bluetoothIface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                    errored = true;
+                }
+
+                mBluetoothPan.setBluetoothTethering(false,
+                        BluetoothPan.NAP_ROLE, BluetoothPan.NAP_BRIDGE);
+
+                mBluetoothSettings.setEnabled(false);
+                if (errored) {
+                    mBluetoothTether.setSummary(R.string.bluetooth_tethering_errored_subtext);
+                } else {
+                    mBluetoothTether.setSummary(R.string.bluetooth_tethering_off_subtext);
+                }
+            }
+        } else if (preference == mTetherHelp) {
             showDialog(DIALOG_TETHER_HELP);
         }
         return false;
index 853fe85..60ae20d 100644 (file)
@@ -133,18 +133,34 @@ public class WirelessSettings extends PreferenceActivity {
         } else {
             String[] usbRegexs = cm.getTetherableUsbRegexs();
             String[] wifiRegexs = cm.getTetherableWifiRegexs();
+            String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
+
+            boolean usbAvailable = usbRegexs.length != 0;
+            boolean wifiAvailable = wifiRegexs.length != 0;
+            boolean bluetoothAvailable = bluetoothRegexs.length != 0;
+
             Preference p = findPreference(KEY_TETHER_SETTINGS);
-            if (wifiRegexs.length == 0) {
+            if (wifiAvailable && usbAvailable && bluetoothAvailable) {
+                p.setTitle(R.string.tether_settings_title_all);
+                p.setSummary(R.string.tether_settings_summary_all);
+            } else if (wifiAvailable && usbAvailable) {
+                p.setTitle(R.string.tether_settings_title_all);
+                p.setSummary(R.string.tether_settings_summary_usb_wifi);
+            } else if (wifiAvailable && bluetoothAvailable) {
+                p.setTitle(R.string.tether_settings_title_all);
+                p.setSummary(R.string.tether_settings_summary_wifi_bluetooth);
+            } else if (wifiAvailable) {
+                p.setTitle(R.string.tether_settings_title_wifi);
+                p.setSummary(R.string.tether_settings_summary_wifi);
+            } else if (usbAvailable && bluetoothAvailable) {
+                p.setTitle(R.string.tether_settings_title_usb_bluetooth);
+                p.setSummary(R.string.tether_settings_summary_usb_bluetooth);
+            } else if (usbAvailable) {
                 p.setTitle(R.string.tether_settings_title_usb);
                 p.setSummary(R.string.tether_settings_summary_usb);
             } else {
-                if (usbRegexs.length == 0) {
-                    p.setTitle(R.string.tether_settings_title_wifi);
-                    p.setSummary(R.string.tether_settings_summary_wifi);
-                } else {
-                    p.setTitle(R.string.tether_settings_title_both);
-                    p.setSummary(R.string.tether_settings_summary_both);
-                }
+                p.setTitle(R.string.tether_settings_title_bluetooth);
+                p.setSummary(R.string.tether_settings_summary_bluetooth);
             }
         }
     }
@@ -152,21 +168,21 @@ public class WirelessSettings extends PreferenceActivity {
     @Override
     protected void onResume() {
         super.onResume();
-        
+
         mAirplaneModeEnabler.resume();
         mWifiEnabler.resume();
         mBtEnabler.resume();
     }
-    
+
     @Override
     protected void onPause() {
         super.onPause();
-        
+
         mAirplaneModeEnabler.pause();
         mWifiEnabler.pause();
         mBtEnabler.pause();
     }
-    
+
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         if (requestCode == REQUEST_CODE_EXIT_ECM) {
index 5b0218f..f0b1705 100644 (file)
@@ -34,6 +34,7 @@ public class BluetoothDevicePreference extends Preference implements CachedBluet
     private static int sDimAlpha = Integer.MIN_VALUE;
 
     private CachedBluetoothDevice mCachedDevice;
+    private int mAccessibleProfile;
 
     /**
      * Cached local copy of whether the device is busy. This is only updated
@@ -41,7 +42,8 @@ public class BluetoothDevicePreference extends Preference implements CachedBluet
      */
     private boolean mIsBusy;
 
-    public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice) {
+    public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice,
+            int accessibleProfile) {
         super(context);
 
         if (sDimAlpha == Integer.MIN_VALUE) {
@@ -51,6 +53,7 @@ public class BluetoothDevicePreference extends Preference implements CachedBluet
         }
 
         mCachedDevice = cachedDevice;
+        mAccessibleProfile = accessibleProfile;
 
         setLayoutResource(R.layout.preference_bluetooth);
 
@@ -83,7 +86,7 @@ public class BluetoothDevicePreference extends Preference implements CachedBluet
          * related to BluetoothHeadset not bound to the actual
          * BluetoothHeadsetService when we got here.
          */
-        setSummary(mCachedDevice.getSummary());
+        setSummary(mCachedDevice.getSummary(mAccessibleProfile));
 
         // Used to gray out the item
         mIsBusy = mCachedDevice.isBusy();
index dc8ab78..f3a404f 100644 (file)
@@ -25,6 +25,7 @@ import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothInputDevice;
+import android.bluetooth.BluetoothPan;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -133,6 +134,18 @@ public class BluetoothEventRedirector {
                 mManager.getCachedDeviceManager().onProfileStateChanged(device,
                         Profile.HID, newState);
 
+            } else if (action.equals(BluetoothPan.ACTION_PAN_STATE_CHANGED)) {
+                final int newState = intent.getIntExtra(
+                        BluetoothPan.EXTRA_PAN_STATE, 0);
+                final int oldState = intent.getIntExtra(
+                        BluetoothPan.EXTRA_PREVIOUS_PAN_STATE, 0);
+                if (newState == BluetoothPan.STATE_DISCONNECTED &&
+                        oldState == BluetoothPan.STATE_CONNECTING) {
+                    Log.i(TAG, "Failed to connect BT PAN");
+                }
+                mManager.getCachedDeviceManager().onProfileStateChanged(device,
+                        Profile.PAN, newState);
+
             } else if (action.equals(BluetoothDevice.ACTION_CLASS_CHANGED)) {
                 mManager.getCachedDeviceManager().onBtClassChanged(device);
 
@@ -180,6 +193,7 @@ public class BluetoothEventRedirector {
         // Fine-grained state broadcasts
         filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
         filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
+        filter.addAction(BluetoothPan.ACTION_PAN_STATE_CHANGED);
         filter.addAction(BluetoothDevice.ACTION_CLASS_CHANGED);
         filter.addAction(BluetoothDevice.ACTION_UUID);
 
index 1e73b2d..82caa46 100644 (file)
@@ -18,22 +18,33 @@ package com.android.settings.bluetooth;
 
 import com.android.settings.ProgressCategory;
 import com.android.settings.R;
+import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
 
+import android.app.AlertDialog;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothDevicePicker;
+import android.bluetooth.BluetoothPan;
 import android.bluetooth.BluetoothUuid;
+import android.bluetooth.IBluetooth;
 import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.preference.CheckBoxPreference;
 import android.preference.Preference;
 import android.preference.PreferenceActivity;
 import android.preference.PreferenceScreen;
+import android.server.BluetoothService;
+import android.text.TextUtils;
+import android.util.Log;
 import android.view.ContextMenu;
 import android.view.MenuItem;
 import android.view.View;
@@ -60,6 +71,10 @@ public class BluetoothSettings extends PreferenceActivity
 
     private static final int SCREEN_TYPE_SETTINGS = 0;
     private static final int SCREEN_TYPE_DEVICEPICKER = 1;
+    private static final int SCREEN_TYPE_TETHERING = 2;
+
+    public static final String ACTION_LAUNCH_TETHER_PICKER =
+        "com.android.settings.bluetooth.action.LAUNCH_TETHER_PICKER";
 
     private int mScreenType;
     private int mFilterType;
@@ -88,16 +103,19 @@ public class BluetoothSettings extends PreferenceActivity
 
             if (intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
                 onBluetoothStateChanged(mLocalManager.getBluetoothState());
-            } else if (intent.getAction().equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
-                    && mScreenType == SCREEN_TYPE_DEVICEPICKER) {
+            } else if (intent.getAction().equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
                 int bondState = intent
                         .getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);
                 if (bondState == BluetoothDevice.BOND_BONDED) {
                     BluetoothDevice device = intent
                             .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                     if (device.equals(mSelectedDevice)) {
-                        sendDevicePickedIntent(device);
-                        finish();
+                        if (mScreenType == SCREEN_TYPE_DEVICEPICKER) {
+                            sendDevicePickedIntent(device);
+                            finish();
+                        } else if (mScreenType == SCREEN_TYPE_TETHERING) {
+                            onPanDevicePicked();
+                        }
                     }
                 }
             }
@@ -134,6 +152,12 @@ public class BluetoothSettings extends PreferenceActivity
 
             setTitle(getString(R.string.device_picker));
             addPreferencesFromResource(R.xml.device_picker);
+        } else if (action.equals(ACTION_LAUNCH_TETHER_PICKER)){
+            mScreenType = SCREEN_TYPE_TETHERING;
+            mFilterType = BluetoothDevicePicker.FILTER_TYPE_PANU;
+
+            setTitle(getString(R.string.device_picker));
+            addPreferencesFromResource(R.xml.device_picker);
         } else {
             addPreferencesFromResource(R.xml.bluetooth_settings);
 
@@ -239,6 +263,18 @@ public class BluetoothSettings extends PreferenceActivity
                 } else {
                     btPreference.getCachedDevice().onClicked();
                 }
+            } else if (mScreenType == SCREEN_TYPE_TETHERING){
+                CachedBluetoothDevice device = btPreference.getCachedDevice();
+
+                mSelectedDevice = device.getDevice();
+                mLocalManager.stopScanning();
+                mLocalManager.persistSelectedDeviceInPicker(mSelectedDevice.getAddress());
+                if ((device.getBondState() == BluetoothDevice.BOND_BONDED)) {
+                    onPanDevicePicked();
+                    // don't call finish so that users can see it connecting
+                } else {
+                    btPreference.getCachedDevice().onClicked();
+                }
             }
             return true;
         }
@@ -321,6 +357,19 @@ public class BluetoothSettings extends PreferenceActivity
                 if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) return true;
             }
             break;
+        case BluetoothDevicePicker.FILTER_TYPE_PANU:
+            if (uuids != null) {
+                if (BluetoothUuid.containsAnyUuid(uuids,
+                        LocalBluetoothProfileManager.PANU_PROFILE_UUIDS))  return true;
+
+            }
+            break;
+        case BluetoothDevicePicker.FILTER_TYPE_NAP:
+            if (uuids != null) {
+                if (BluetoothUuid.containsAnyUuid(uuids,
+                        LocalBluetoothProfileManager.NAP_PROFILE_UUIDS))  return true;
+            }
+            break;
         default:
             return true;
         }
@@ -328,7 +377,14 @@ public class BluetoothSettings extends PreferenceActivity
     }
 
     private void createDevicePreference(CachedBluetoothDevice cachedDevice) {
-        BluetoothDevicePreference preference = new BluetoothDevicePreference(this, cachedDevice);
+        BluetoothDevicePreference preference;
+        if (mScreenType == SCREEN_TYPE_TETHERING) {
+            preference = new BluetoothDevicePreference(
+                    this, cachedDevice, CachedBluetoothDevice.PAN_PROFILE);
+        } else {
+            preference = new BluetoothDevicePreference(
+                    this, cachedDevice, CachedBluetoothDevice.OTHER_PROFILES);
+        }
         mDeviceList.addPreference(preference);
         mDevicePreferenceMap.put(cachedDevice, preference);
     }
@@ -354,12 +410,52 @@ public class BluetoothSettings extends PreferenceActivity
         }
     }
 
+    private void onPanDevicePicked() {
+        final LocalBluetoothProfileManager profileManager =
+            LocalBluetoothProfileManager.getProfileManager(mLocalManager, Profile.PAN);
+        int status = profileManager.getConnectionStatus(mSelectedDevice);
+        if (SettingsBtStatus.isConnectionStatusConnected(status)) {
+            String name = mSelectedDevice.getName();
+            if (TextUtils.isEmpty(name)) {
+                name = getString(R.string.bluetooth_device);
+            }
+            String message = getString(R.string.bluetooth_untether_blank, name);
+            DialogInterface.OnClickListener disconnectListener =
+                new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int which) {
+                    profileManager.disconnect(mSelectedDevice);
+                }
+            };
+            new AlertDialog.Builder(this)
+                .setTitle(name)
+                .setMessage(message)
+                .setPositiveButton(android.R.string.ok, disconnectListener)
+                .setNegativeButton(android.R.string.cancel, null)
+                .create()
+                .show();
+        } else if (status == SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED) {
+            if (profileManager.getConnectedDevices().size() >= BluetoothPan.MAX_CONNECTIONS) {
+                new AlertDialog.Builder(this)
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .setTitle(R.string.bluetooth_error_title)
+                    .setMessage(getString(R.string.bluetooth_tethering_overflow_error,
+                            BluetoothPan.MAX_CONNECTIONS))
+                    .setNegativeButton(android.R.string.ok, null)
+                    .create()
+                    .show();
+                return;
+            }
+            profileManager.connect(mSelectedDevice);
+        }
+    }
+
     private void sendDevicePickedIntent(BluetoothDevice device) {
         Intent intent = new Intent(BluetoothDevicePicker.ACTION_DEVICE_SELECTED);
-        if (mLaunchPackage != null && mLaunchClass != null) {
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        if (mScreenType == SCREEN_TYPE_DEVICEPICKER &&
+                mLaunchPackage != null && mLaunchClass != null) {
             intent.setClassName(mLaunchPackage, mLaunchClass);
         }
-        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
         sendBroadcast(intent);
     }
 }
index bdaffca..5f374a5 100644 (file)
@@ -56,6 +56,9 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
     private static final int CONTEXT_ITEM_UNPAIR = Menu.FIRST + 3;
     private static final int CONTEXT_ITEM_CONNECT_ADVANCED = Menu.FIRST + 4;
 
+    public static final int PAN_PROFILE = 1;
+    public static final int OTHER_PROFILES = 2;
+
     private final BluetoothDevice mDevice;
     private String mName;
     private short mRssi;
@@ -625,9 +628,9 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
         }
     }
 
-    public int getSummary() {
+    public int getSummary(int accessibleProfile) {
         // TODO: clean up
-        int oneOffSummary = getOneOffSummary();
+        int oneOffSummary = getOneOffSummary(accessibleProfile);
         if (oneOffSummary != 0) {
             return oneOffSummary;
         }
@@ -653,34 +656,43 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
      *
      * @return A one-off summary that is applicable for the current state, or 0.
      */
-    private int getOneOffSummary() {
+    private int getOneOffSummary(int accessibleProfile) {
         boolean isA2dpConnected = false;
         boolean isHeadsetConnected = false;
         boolean isHidConnected = false;
+        boolean isPanConnected = false;
         boolean isConnecting = false;
 
-        if (mProfiles.contains(Profile.A2DP)) {
-            LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
-                    .getProfileManager(mLocalManager, Profile.A2DP);
-            isConnecting = profileManager.getConnectionStatus(mDevice) ==
-                    SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
-            isA2dpConnected = profileManager.isConnected(mDevice);
-        }
+        if (accessibleProfile == OTHER_PROFILES) {
+            if (mProfiles.contains(Profile.A2DP)) {
+                LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
+                        .getProfileManager(mLocalManager, Profile.A2DP);
+                isConnecting = profileManager.getConnectionStatus(mDevice) ==
+                        SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
+                isA2dpConnected = profileManager.isConnected(mDevice);
+            }
 
-        if (mProfiles.contains(Profile.HEADSET)) {
-            LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
-                    .getProfileManager(mLocalManager, Profile.HEADSET);
-            isConnecting |= profileManager.getConnectionStatus(mDevice) ==
-                    SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
-            isHeadsetConnected = profileManager.isConnected(mDevice);
-        }
+            if (mProfiles.contains(Profile.HEADSET)) {
+                LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
+                        .getProfileManager(mLocalManager, Profile.HEADSET);
+                isConnecting |= profileManager.getConnectionStatus(mDevice) ==
+                        SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
+                isHeadsetConnected = profileManager.isConnected(mDevice);
+            }
 
-        if (mProfiles.contains(Profile.HID)) {
+            if (mProfiles.contains(Profile.HID)) {
+                LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
+                        .getProfileManager(mLocalManager, Profile.HID);
+                isConnecting |= profileManager.getConnectionStatus(mDevice) ==
+                        SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
+                isHidConnected = profileManager.isConnected(mDevice);
+            }
+        } else if (accessibleProfile == PAN_PROFILE && mProfiles.contains(Profile.PAN)) {
             LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
-                    .getProfileManager(mLocalManager, Profile.HID);
+                    .getProfileManager(mLocalManager, Profile.PAN);
             isConnecting |= profileManager.getConnectionStatus(mDevice) ==
                     SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
-            isHidConnected = profileManager.isConnected(mDevice);
+            isPanConnected = profileManager.isConnected(mDevice);
         }
 
         if (isConnecting) {
@@ -695,6 +707,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
             return R.string.bluetooth_summary_connected_to_headset;
         } else if (isHidConnected) {
             return R.string.bluetooth_summary_connected_to_hid;
+        } else if (isPanConnected) {
+            return R.string.bluetooth_summary_connected_to_pan;
         } else {
             return 0;
         }
index 62c6f79..ab769a7 100644 (file)
@@ -232,7 +232,8 @@ public class ConnectSpecificProfilesActivity extends PreferenceActivity
          * If the device is online, show status. Otherwise, show a summary that
          * describes what the checkbox does.
          */
-        mOnlineModePreference.setSummary(mOnlineMode ? mCachedDevice.getSummary()
+        mOnlineModePreference.setSummary(mOnlineMode ?
+                mCachedDevice.getSummary(CachedBluetoothDevice.OTHER_PROFILES)
                 : R.string.bluetooth_device_advanced_online_mode_summary);
     }
 
index b09e144..6193a4e 100644 (file)
@@ -22,6 +22,7 @@ import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothInputDevice;
+import android.bluetooth.BluetoothPan;
 import android.bluetooth.BluetoothUuid;
 import android.os.Handler;
 import android.os.ParcelUuid;
@@ -60,6 +61,14 @@ public abstract class LocalBluetoothProfileManager {
         BluetoothUuid.Hid
     };
 
+    /* package */ static final ParcelUuid[] PANU_PROFILE_UUIDS = new ParcelUuid[] {
+        BluetoothUuid.PANU
+    };
+
+    /* package */ static final ParcelUuid[] NAP_PROFILE_UUIDS = new ParcelUuid[] {
+        BluetoothUuid.NAP
+    };
+
     /**
      * An interface for notifying BluetoothHeadset IPC clients when they have
      * been connected to the BluetoothHeadset service.
@@ -105,6 +114,9 @@ public abstract class LocalBluetoothProfileManager {
 
                 profileManager = new HidProfileManager(localManager);
                 sProfileMap.put(Profile.HID, profileManager);
+
+                profileManager = new PanProfileManager(localManager);
+                sProfileMap.put(Profile.PAN, profileManager);
             }
         }
     }
@@ -173,6 +185,10 @@ public abstract class LocalBluetoothProfileManager {
         if (BluetoothUuid.containsAnyUuid(uuids, HID_PROFILE_UUIDS)) {
             profiles.add(Profile.HID);
         }
+
+        if (BluetoothUuid.containsAnyUuid(uuids, PANU_PROFILE_UUIDS)) {
+            profiles.add(Profile.PAN);
+        }
     }
 
     protected LocalBluetoothProfileManager(LocalBluetoothManager localManager) {
@@ -208,7 +224,8 @@ public abstract class LocalBluetoothProfileManager {
         HEADSET(R.string.bluetooth_profile_headset),
         A2DP(R.string.bluetooth_profile_a2dp),
         OPP(R.string.bluetooth_profile_opp),
-        HID(R.string.bluetooth_profile_hid);
+        HID(R.string.bluetooth_profile_hid),
+        PAN(R.string.bluetooth_profile_pan);
 
         public final int localizedString;
 
@@ -613,4 +630,80 @@ public abstract class LocalBluetoothProfileManager {
             }
         }
     }
+
+    private static class PanProfileManager extends LocalBluetoothProfileManager {
+        private BluetoothPan mService;
+
+        public PanProfileManager(LocalBluetoothManager localManager) {
+            super(localManager);
+            mService = new BluetoothPan(localManager.getContext());
+        }
+
+        @Override
+        public boolean connect(BluetoothDevice device) {
+            return mService.connect(device);
+        }
+
+        @Override
+        public int convertState(int panState) {
+            switch (panState) {
+            case BluetoothPan.STATE_CONNECTED:
+                return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
+            case BluetoothPan.STATE_CONNECTING:
+                return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
+            case BluetoothPan.STATE_DISCONNECTED:
+                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
+            case BluetoothPan.STATE_DISCONNECTING:
+                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING;
+            default:
+                return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
+            }
+        }
+
+        @Override
+        public boolean disconnect(BluetoothDevice device) {
+            return mService.disconnect(device);
+        }
+
+        @Override
+        public int getSummary(BluetoothDevice device) {
+            final int connectionStatus = getConnectionStatus(device);
+
+            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
+                return R.string.bluetooth_pan_profile_summary_connected;
+            } else {
+                return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
+            }
+        }
+
+        @Override
+        public boolean isProfileReady() {
+            return true;
+        }
+
+        @Override
+        public Set<BluetoothDevice> getConnectedDevices() {
+            return mService.getConnectedDevices();
+        }
+
+        @Override
+        public int getConnectionStatus(BluetoothDevice device) {
+            return convertState(mService.getPanDeviceState(device));
+        }
+
+        @Override
+        public int getPreferred(BluetoothDevice device) {
+            return -1;
+        }
+
+        @Override
+        public boolean isPreferred(BluetoothDevice device) {
+            return false;
+        }
+
+        @Override
+        public void setPreferred(BluetoothDevice device, boolean preferred) {
+            return;
+        }
+    }
 }