diff --git a/res/values/strings.xml b/res/values/strings.xml
index c84565cc80a..4c6210209f7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -950,16 +950,20 @@
Input Device
- Tethering
+ Internet access
-
+
%1$s will be disconnected from media audio.
-
+
%1$s will be disconnected from handsfree audio.
-
+
%1$s will be disconnected from input device.
-
- %1$s will be disconnected from tethering.
+
+ Internet access via %1$s will be disconnected.
+
+ %1$s will be disconnected from sharing this tablet\'s Internet connection.
+
+ %1$s will be disconnected from sharing this phone\'s Internet connection.
%1$s options
@@ -985,8 +989,10 @@
Not connected to file transfer server
Connected to input device
-
- Tethered
+
+ Connected to device for Internet access
+
+ Sharing local Internet connection with device
Use for media audio
@@ -1863,11 +1869,21 @@
Bluetooth tethering
- Bluetooth tethering on, but not connected
-
- Bluetooth tethering on and connected
+ Sharing this tablet\'s Internet connection
+
+ Sharing this phone\'s Internet connection
+
+ Sharing this tablet\'s Internet connection to 1 device
+
+ Sharing this phone\'s Internet connection to 1 device
+
+ Sharing this tablet\'s Internet connection to %1$d devices
+
+ Sharing this phone\'s Internet connection to %1$d devices
- Bluetooth tethering off
+ Not sharing this tablet\'s Internet connection
+
+ Not sharing this phone\'s Internet connection
Bluetooth tethering error
diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java
index 39f05356ffb..1513d43cf91 100644
--- a/src/com/android/settings/TetherSettings.java
+++ b/src/com/android/settings/TetherSettings.java
@@ -95,6 +95,8 @@ public class TetherSettings extends SettingsPreferenceFragment
private WifiManager mWifiManager;
private WifiConfiguration mWifiConfig = null;
+ private boolean mBluetoothEnableForTether;
+
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -239,7 +241,8 @@ public class TetherSettings extends SettingsPreferenceFragment
private class TetherChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context content, Intent intent) {
- if (intent.getAction().equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)) {
+ String action = intent.getAction();
+ if (action.equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)) {
// TODO - this should understand the interface types
ArrayList available = intent.getStringArrayListExtra(
ConnectivityManager.EXTRA_AVAILABLE_TETHER);
@@ -250,10 +253,27 @@ public class TetherSettings extends SettingsPreferenceFragment
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)) {
+ } else if (action.equals(Intent.ACTION_MEDIA_SHARED) ||
+ action.equals(Intent.ACTION_MEDIA_UNSHARED)) {
updateState();
- } else if (intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+ } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+ if (mBluetoothEnableForTether) {
+ switch (intent
+ .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {
+ case BluetoothAdapter.STATE_ON:
+ mBluetoothPan.setBluetoothTethering(true);
+ mBluetoothEnableForTether = false;
+ break;
+
+ case BluetoothAdapter.STATE_OFF:
+ case BluetoothAdapter.ERROR:
+ mBluetoothEnableForTether = false;
+ break;
+
+ default:
+ // ignore transition states
+ }
+ }
updateState();
}
}
@@ -281,6 +301,8 @@ public class TetherSettings extends SettingsPreferenceFragment
if (intent != null) mTetherChangeReceiver.onReceive(activity, intent);
mWifiApEnabler.resume();
+
+ updateState();
}
@Override
@@ -368,22 +390,10 @@ public class TetherSettings extends SettingsPreferenceFragment
private void updateBluetoothState(String[] available, String[] tethered,
String[] errored) {
- ConnectivityManager cm =
- (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
- int bluetoothError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
- for (String s : available) {
- for (String regex : mBluetoothRegexs) {
- if (s.matches(regex)) {
- if (bluetoothError == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
- bluetoothError = cm.getLastTetherError(s);
- }
- }
- }
- }
- boolean bluetoothTethered = false;
+ int bluetoothTethered = 0;
for (String s : tethered) {
for (String regex : mBluetoothRegexs) {
- if (s.matches(regex)) bluetoothTethered = true;
+ if (s.matches(regex)) bluetoothTethered++;
}
}
boolean bluetoothErrored = false;
@@ -401,17 +411,19 @@ public class TetherSettings extends SettingsPreferenceFragment
} else if (btState == BluetoothAdapter.STATE_TURNING_ON) {
mBluetoothTether.setEnabled(false);
mBluetoothTether.setSummary(R.string.bluetooth_turning_on);
- } else if (mBluetoothPan.isTetheringOn()) {
+ } else if (btState == BluetoothAdapter.STATE_ON && mBluetoothPan.isTetheringOn()) {
mBluetoothTether.setChecked(true);
- if (btState == BluetoothAdapter.STATE_ON) {
- mBluetoothTether.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);
- }
+ mBluetoothTether.setEnabled(true);
+ if (bluetoothTethered > 1) {
+ String summary = getString(
+ R.string.bluetooth_tethering_devices_connected_subtext, bluetoothTethered);
+ mBluetoothTether.setSummary(summary);
+ } else if (bluetoothTethered == 1) {
+ mBluetoothTether.setSummary(R.string.bluetooth_tethering_device_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);
@@ -456,20 +468,21 @@ public class TetherSettings extends SettingsPreferenceFragment
}
mUsbTether.setSummary("");
}
- } else if(preference == mBluetoothTether) {
+ } else if (preference == mBluetoothTether) {
boolean bluetoothTetherState = mBluetoothTether.isChecked();
if (bluetoothTetherState) {
// turn on Bluetooth first
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter.getState() == BluetoothAdapter.STATE_OFF) {
+ mBluetoothEnableForTether = true;
adapter.enable();
mBluetoothTether.setSummary(R.string.bluetooth_turning_on);
mBluetoothTether.setEnabled(false);
+ } else {
+ mBluetoothPan.setBluetoothTethering(true);
+ mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext);
}
-
- mBluetoothPan.setBluetoothTethering(true);
- mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext);
} else {
boolean errored = false;
diff --git a/src/com/android/settings/bluetooth/A2dpProfile.java b/src/com/android/settings/bluetooth/A2dpProfile.java
index 96225d87c3e..e8582f3799e 100644
--- a/src/com/android/settings/bluetooth/A2dpProfile.java
+++ b/src/com/android/settings/bluetooth/A2dpProfile.java
@@ -142,7 +142,7 @@ final class A2dpProfile implements LocalBluetoothProfile {
return R.string.bluetooth_profile_a2dp;
}
- public int getDisconnectResource() {
+ public int getDisconnectResource(BluetoothDevice device) {
return R.string.bluetooth_disconnect_a2dp_profile;
}
diff --git a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
index 56e96b4d003..71a5c01740c 100644
--- a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
+++ b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
@@ -51,6 +51,13 @@ final class CachedBluetoothDevice implements Comparable {
private final List mProfiles =
new ArrayList();
+ // List of profiles that were previously in mProfiles, but have been removed
+ private final List mRemovedProfiles =
+ new ArrayList();
+
+ // Device supports PANU but not NAP: remove PanProfile after device disconnects from NAP
+ private boolean mLocalNapRoleConnected;
+
private boolean mVisible;
private final Collection mCallbacks = new ArrayList();
@@ -100,8 +107,21 @@ final class CachedBluetoothDevice implements Comparable {
mProfileConnectionState.put(profile, newProfileState);
if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
if (!mProfiles.contains(profile)) {
+ mRemovedProfiles.remove(profile);
mProfiles.add(profile);
+ if (profile instanceof PanProfile &&
+ ((PanProfile) profile).isLocalRoleNap(mDevice)) {
+ // Device doesn't support NAP, so remove PanProfile on disconnect
+ mLocalNapRoleConnected = true;
+ }
}
+ } else if (mLocalNapRoleConnected && profile instanceof PanProfile &&
+ ((PanProfile) profile).isLocalRoleNap(mDevice) &&
+ newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
+ Log.d(TAG, "Removing PanProfile from device after NAP disconnect");
+ mProfiles.remove(profile);
+ mRemovedProfiles.add(profile);
+ mLocalNapRoleConnected = false;
}
}
@@ -391,7 +411,7 @@ final class CachedBluetoothDevice implements Comparable {
ParcelUuid[] localUuids = mLocalAdapter.getUuids();
if (localUuids == null) return false;
- mProfileManager.updateProfiles(uuids, localUuids, mProfiles);
+ mProfileManager.updateProfiles(uuids, localUuids, mProfiles, mRemovedProfiles);
if (DEBUG) {
Log.e(TAG, "updating profiles for " + mDevice.getName());
@@ -482,6 +502,10 @@ final class CachedBluetoothDevice implements Comparable {
return connectableProfiles;
}
+ List getRemovedProfiles() {
+ return mRemovedProfiles;
+ }
+
void registerCallback(Callback callback) {
synchronized (mCallbacks) {
mCallbacks.add(callback);
diff --git a/src/com/android/settings/bluetooth/DeviceProfilesSettings.java b/src/com/android/settings/bluetooth/DeviceProfilesSettings.java
index 307125cb5d5..9db4baf4e11 100644
--- a/src/com/android/settings/bluetooth/DeviceProfilesSettings.java
+++ b/src/com/android/settings/bluetooth/DeviceProfilesSettings.java
@@ -239,7 +239,7 @@ public final class DeviceProfilesSettings extends SettingsPreferenceFragment
if (TextUtils.isEmpty(name)) {
name = context.getString(R.string.bluetooth_device);
}
- int disconnectMessage = profile.getDisconnectResource();
+ int disconnectMessage = profile.getDisconnectResource(device.getDevice());
if (disconnectMessage == 0) {
Log.w(TAG, "askDisconnect: unexpected profile " + profile);
disconnectMessage = R.string.bluetooth_disconnect_blank;
@@ -288,6 +288,13 @@ public final class DeviceProfilesSettings extends SettingsPreferenceFragment
refreshProfilePreference(profilePref, profile);
}
}
+ for (LocalBluetoothProfile profile : mCachedDevice.getRemovedProfiles()) {
+ Preference profilePref = findPreference(profile.toString());
+ if (profilePref != null) {
+ Log.d(TAG, "Removing " + profile.toString() + " from profile list");
+ mProfileContainer.removePreference(profilePref);
+ }
+ }
}
private void refreshProfilePreference(Preference profilePref, LocalBluetoothProfile profile) {
diff --git a/src/com/android/settings/bluetooth/HeadsetProfile.java b/src/com/android/settings/bluetooth/HeadsetProfile.java
index e9c52ef0bb4..13dce335cf2 100644
--- a/src/com/android/settings/bluetooth/HeadsetProfile.java
+++ b/src/com/android/settings/bluetooth/HeadsetProfile.java
@@ -22,13 +22,11 @@ import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.content.Context;
-import android.os.Handler;
import android.os.ParcelUuid;
import android.util.Log;
import com.android.settings.R;
-import java.util.ArrayList;
import java.util.List;
/**
@@ -176,7 +174,7 @@ final class HeadsetProfile implements LocalBluetoothProfile {
return R.string.bluetooth_profile_headset;
}
- public int getDisconnectResource() {
+ public int getDisconnectResource(BluetoothDevice device) {
return R.string.bluetooth_disconnect_headset_profile;
}
diff --git a/src/com/android/settings/bluetooth/HidProfile.java b/src/com/android/settings/bluetooth/HidProfile.java
index 9185059d6b9..13d3db9dffe 100644
--- a/src/com/android/settings/bluetooth/HidProfile.java
+++ b/src/com/android/settings/bluetooth/HidProfile.java
@@ -116,7 +116,7 @@ final class HidProfile implements LocalBluetoothProfile {
return R.string.bluetooth_profile_hid;
}
- public int getDisconnectResource() {
+ public int getDisconnectResource(BluetoothDevice device) {
return R.string.bluetooth_disconnect_hid_profile;
}
diff --git a/src/com/android/settings/bluetooth/LocalBluetoothProfile.java b/src/com/android/settings/bluetooth/LocalBluetoothProfile.java
index 936231a1c14..878a032b443 100644
--- a/src/com/android/settings/bluetooth/LocalBluetoothProfile.java
+++ b/src/com/android/settings/bluetooth/LocalBluetoothProfile.java
@@ -60,8 +60,9 @@ interface LocalBluetoothProfile {
/**
* Returns the string resource ID for the disconnect confirmation text
* for this profile.
+ * @param device
*/
- int getDisconnectResource();
+ int getDisconnectResource(BluetoothDevice device);
/**
* Returns the string resource ID for the summary text for this profile
diff --git a/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java b/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java
index ee3cb66a067..f3143f0b8e7 100644
--- a/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java
+++ b/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java
@@ -110,7 +110,9 @@ final class LocalBluetoothProfileManager {
BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
mPanProfile = new PanProfile(context);
- addProfile(mPanProfile, PanProfile.NAME, BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
+ addPanProfile(mPanProfile, PanProfile.NAME,
+ BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
+
Log.d(TAG, "LocalBluetoothProfileManager construction complete");
}
@@ -173,6 +175,13 @@ final class LocalBluetoothProfileManager {
mProfileNameMap.put(profileName, profile);
}
+ private void addPanProfile(LocalBluetoothProfile profile,
+ String profileName, String stateChangedAction) {
+ mEventManager.addProfileHandler(stateChangedAction,
+ new PanStateChangedHandler(profile));
+ mProfileNameMap.put(profileName, profile);
+ }
+
LocalBluetoothProfile getProfileByName(String name) {
return mProfileNameMap.get(name);
}
@@ -190,7 +199,7 @@ final class LocalBluetoothProfileManager {
* Generic handler for connection state change events for the specified profile.
*/
private class StateChangedHandler implements BluetoothEventManager.Handler {
- private final LocalBluetoothProfile mProfile;
+ final LocalBluetoothProfile mProfile;
StateChangedHandler(LocalBluetoothProfile profile) {
mProfile = profile;
@@ -215,6 +224,22 @@ final class LocalBluetoothProfileManager {
}
}
+ /** State change handler for NAP and PANU profiles. */
+ private class PanStateChangedHandler extends StateChangedHandler {
+
+ PanStateChangedHandler(LocalBluetoothProfile profile) {
+ super(profile);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ PanProfile panProfile = (PanProfile) mProfile;
+ int role = intent.getIntExtra(BluetoothPan.EXTRA_LOCAL_ROLE, 0);
+ panProfile.setLocalRole(device, role);
+ super.onReceive(context, intent, device);
+ }
+ }
+
// called from DockService
void addServiceListener(ServiceListener l) {
mServiceListeners.add(l);
@@ -269,9 +294,14 @@ final class LocalBluetoothProfileManager {
* @param uuids of the remote device
* @param localUuids UUIDs of the local device
* @param profiles The list of profiles to fill
+ * @param removedProfiles list of profiles that were removed
*/
synchronized void updateProfiles(ParcelUuid[] uuids, ParcelUuid[] localUuids,
- Collection profiles) {
+ Collection profiles,
+ Collection removedProfiles) {
+ // Copy previous profile list into removedProfiles
+ removedProfiles.clear();
+ removedProfiles.addAll(profiles);
profiles.clear();
if (uuids == null) {
@@ -280,31 +310,36 @@ final class LocalBluetoothProfileManager {
if (mHeadsetProfile != null) {
if ((BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.HSP_AG) &&
- BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP)) ||
- (BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree_AG) &&
- BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree))) {
- profiles.add(mHeadsetProfile);
+ BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP)) ||
+ (BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree_AG) &&
+ BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree))) {
+ profiles.add(mHeadsetProfile);
+ removedProfiles.remove(mHeadsetProfile);
}
}
if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS) &&
mA2dpProfile != null) {
profiles.add(mA2dpProfile);
+ removedProfiles.remove(mA2dpProfile);
}
if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush) &&
mOppProfile != null) {
profiles.add(mOppProfile);
+ removedProfiles.remove(mOppProfile);
}
if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hid) &&
mHidProfile != null) {
profiles.add(mHidProfile);
+ removedProfiles.remove(mHidProfile);
}
if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP) &&
mPanProfile != null) {
profiles.add(mPanProfile);
+ removedProfiles.remove(mPanProfile);
}
}
}
diff --git a/src/com/android/settings/bluetooth/OppProfile.java b/src/com/android/settings/bluetooth/OppProfile.java
index 3f7df38532b..eb5900e0e14 100644
--- a/src/com/android/settings/bluetooth/OppProfile.java
+++ b/src/com/android/settings/bluetooth/OppProfile.java
@@ -79,7 +79,7 @@ final class OppProfile implements LocalBluetoothProfile {
return R.string.bluetooth_profile_opp;
}
- public int getDisconnectResource() {
+ public int getDisconnectResource(BluetoothDevice device) {
return 0; // user must use notification to disconnect OPP transfer.
}
diff --git a/src/com/android/settings/bluetooth/PanProfile.java b/src/com/android/settings/bluetooth/PanProfile.java
index 3f456e45bc5..6cb1991efbd 100644
--- a/src/com/android/settings/bluetooth/PanProfile.java
+++ b/src/com/android/settings/bluetooth/PanProfile.java
@@ -25,14 +25,19 @@ import android.content.Context;
import com.android.settings.R;
+import java.util.HashMap;
import java.util.List;
/**
- * PanProfile handles Bluetooth PAN profile.
+ * PanProfile handles Bluetooth PAN profile (NAP and PANU).
*/
final class PanProfile implements LocalBluetoothProfile {
private BluetoothPan mService;
+ // Tethering direction for each device
+ private final HashMap mDeviceRoleMap =
+ new HashMap();
+
static final String NAME = "PAN";
// Order of this profile in device profiles list
@@ -111,8 +116,12 @@ final class PanProfile implements LocalBluetoothProfile {
return R.string.bluetooth_profile_pan;
}
- public int getDisconnectResource() {
- return R.string.bluetooth_disconnect_pan_profile;
+ public int getDisconnectResource(BluetoothDevice device) {
+ if (isLocalRoleNap(device)) {
+ return R.string.bluetooth_disconnect_pan_nap_profile;
+ } else {
+ return R.string.bluetooth_disconnect_pan_user_profile;
+ }
}
public int getSummaryResourceForDevice(BluetoothDevice device) {
@@ -122,7 +131,11 @@ final class PanProfile implements LocalBluetoothProfile {
return R.string.bluetooth_pan_profile_summary_use_for;
case BluetoothProfile.STATE_CONNECTED:
- return R.string.bluetooth_pan_profile_summary_connected;
+ if (isLocalRoleNap(device)) {
+ return R.string.bluetooth_pan_nap_profile_summary_connected;
+ } else {
+ return R.string.bluetooth_pan_user_profile_summary_connected;
+ }
default:
return Utils.getConnectionStateSummary(state);
@@ -132,4 +145,17 @@ final class PanProfile implements LocalBluetoothProfile {
public int getDrawableResource(BluetoothClass btClass) {
return R.drawable.ic_bt_network_pan;
}
+
+ // Tethering direction determines UI strings.
+ void setLocalRole(BluetoothDevice device, int role) {
+ mDeviceRoleMap.put(device, role);
+ }
+
+ boolean isLocalRoleNap(BluetoothDevice device) {
+ if (mDeviceRoleMap.containsKey(device)) {
+ return mDeviceRoleMap.get(device) == BluetoothPan.LOCAL_NAP_ROLE;
+ } else {
+ return false;
+ }
+ }
}