Merge "Expose a simple tethering API which includes provision checks."
diff --git a/api/system-current.txt b/api/system-current.txt
index e4f42c0..dbaecd7 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -24485,6 +24485,7 @@
method public boolean isActiveNetworkMetered();
method public boolean isDefaultNetworkActive();
method public static deprecated boolean isNetworkTypeValid(int);
+ method public boolean isTetheringSupported();
method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
method public void registerNetworkCallback(android.net.NetworkRequest, android.app.PendingIntent);
method public void releaseNetworkRequest(android.app.PendingIntent);
@@ -24497,7 +24498,10 @@
method public deprecated boolean requestRouteToHost(int, int);
method public deprecated void setNetworkPreference(int);
method public static deprecated boolean setProcessDefaultNetwork(android.net.Network);
+ method public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
+ method public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
method public deprecated int startUsingNetworkFeature(int, java.lang.String);
+ method public void stopTethering(int);
method public deprecated int stopUsingNetworkFeature(int, java.lang.String);
method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
method public void unregisterNetworkCallback(android.app.PendingIntent);
@@ -24519,6 +24523,9 @@
field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
+ field public static final int TETHERING_BLUETOOTH = 2; // 0x2
+ field public static final int TETHERING_USB = 1; // 0x1
+ field public static final int TETHERING_WIFI = 0; // 0x0
field public static final int TYPE_BLUETOOTH = 7; // 0x7
field public static final int TYPE_DUMMY = 8; // 0x8
field public static final int TYPE_ETHERNET = 9; // 0x9
@@ -24545,6 +24552,12 @@
method public abstract void onNetworkActive();
}
+ public static abstract class ConnectivityManager.OnStartTetheringCallback {
+ ctor public ConnectivityManager.OnStartTetheringCallback();
+ method public void onTetheringFailed();
+ method public void onTetheringStarted();
+ }
+
public class Credentials {
ctor public Credentials(int, int, int);
method public int getGid();
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 08c0c09..7cb086f 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -16,6 +16,7 @@
package android.net;
import static com.android.internal.util.Preconditions.checkNotNull;
+
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
@@ -27,6 +28,7 @@
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -37,6 +39,7 @@
import android.os.Messenger;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
@@ -338,6 +341,71 @@
public static final String ACTION_PROMPT_UNVALIDATED = "android.net.conn.PROMPT_UNVALIDATED";
/**
+ * Invalid tethering type.
+ * @see #startTethering(int, OnStartTetheringCallback, boolean)
+ * @hide
+ */
+ public static final int TETHERING_INVALID = -1;
+
+ /**
+ * Wifi tethering type.
+ * @see #startTethering(int, OnStartTetheringCallback, boolean)
+ * @hide
+ */
+ @SystemApi
+ public static final int TETHERING_WIFI = 0;
+
+ /**
+ * USB tethering type.
+ * @see #startTethering(int, OnStartTetheringCallback, boolean)
+ * @hide
+ */
+ @SystemApi
+ public static final int TETHERING_USB = 1;
+
+ /**
+ * Bluetooth tethering type.
+ * @see #startTethering(int, OnStartTetheringCallback, boolean)
+ * @hide
+ */
+ @SystemApi
+ public static final int TETHERING_BLUETOOTH = 2;
+
+ /**
+ * Extra used for communicating with the TetherService. Includes the type of tethering to
+ * enable if any.
+ * @hide
+ */
+ public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
+
+ /**
+ * Extra used for communicating with the TetherService. Includes the type of tethering for
+ * which to cancel provisioning.
+ * @hide
+ */
+ public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
+
+ /**
+ * Extra used for communicating with the TetherService. True to schedule a recheck of tether
+ * provisioning.
+ * @hide
+ */
+ public static final String EXTRA_SET_ALARM = "extraSetAlarm";
+
+ /**
+ * Tells the TetherService to run a provision check now.
+ * @hide
+ */
+ public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
+
+ /**
+ * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver}
+ * which will receive provisioning results. Can be left empty.
+ * @hide
+ */
+ public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
+
+ /**
* The absence of a connection type.
* @hide
*/
@@ -1789,6 +1857,11 @@
* or the ability to modify system settings as determined by
* {@link android.provider.Settings.System#canWrite}.</p>
*
+ * <p>WARNING: New clients should not use this function. The only usages should be in PanService
+ * and WifiStateMachine which need direct access. All other clients should use
+ * {@link #startTethering} and {@link #stopTethering} which encapsulate proper provisioning
+ * logic.</p>
+ *
* @param iface the interface name to tether.
* @return error a {@code TETHER_ERROR} value indicating success or failure type
*
@@ -1810,6 +1883,11 @@
* or the ability to modify system settings as determined by
* {@link android.provider.Settings.System#canWrite}.</p>
*
+ * <p>WARNING: New clients should not use this function. The only usages should be in PanService
+ * and WifiStateMachine which need direct access. All other clients should use
+ * {@link #startTethering} and {@link #stopTethering} which encapsulate proper provisioning
+ * logic.</p>
+ *
* @param iface the interface name to untether.
* @return error a {@code TETHER_ERROR} value indicating success or failure type
*
@@ -1834,6 +1912,7 @@
*
* {@hide}
*/
+ @SystemApi
public boolean isTetheringSupported() {
try {
return mService.isTetheringSupported();
@@ -1843,6 +1922,94 @@
}
/**
+ * Callback for use with {@link #startTethering} to find out whether tethering succeeded.
+ * @hide
+ */
+ @SystemApi
+ public static abstract class OnStartTetheringCallback {
+ /**
+ * Called when tethering has been successfully started.
+ */
+ public void onTetheringStarted() {};
+
+ /**
+ * Called when starting tethering failed.
+ */
+ public void onTetheringFailed() {};
+ }
+
+ /**
+ * Convenient overload for
+ * {@link #startTethering(int, boolean, OnStartTetheringCallback, Handler)} which passes a null
+ * handler to run on the current thread's {@link Looper}.
+ * @hide
+ */
+ @SystemApi
+ public void startTethering(int type, boolean showProvisioningUi,
+ final OnStartTetheringCallback callback) {
+ startTethering(type, showProvisioningUi, callback, null);
+ }
+
+ /**
+ * Runs tether provisioning for the given type if needed and then starts tethering if
+ * the check succeeds. If no carrier provisioning is required for tethering, tethering is
+ * enabled immediately. If provisioning fails, tethering will not be enabled. It also
+ * schedules tether provisioning re-checks if appropriate.
+ *
+ * @param type The type of tethering to start. Must be one of
+ * {@link ConnectivityManager.TETHERING_WIFI},
+ * {@link ConnectivityManager.TETHERING_USB}, or
+ * {@link ConnectivityManager.TETHERING_BLUETOOTH}.
+ * @param showProvisioningUi a boolean indicating to show the provisioning app UI if there
+ * is one. This should be true the first time this function is called and also any time
+ * the user can see this UI. It gives users information from their carrier about the
+ * check failing and how they can sign up for tethering if possible.
+ * @param callback an {@link OnStartTetheringCallback} which will be called to notify the caller
+ * of the result of trying to tether.
+ * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ * @hide
+ */
+ @SystemApi
+ public void startTethering(int type, boolean showProvisioningUi,
+ final OnStartTetheringCallback callback, Handler handler) {
+ ResultReceiver wrappedCallback = new ResultReceiver(handler) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode == TETHER_ERROR_NO_ERROR) {
+ callback.onTetheringStarted();
+ } else {
+ callback.onTetheringFailed();
+ }
+ }
+ };
+ try {
+ mService.startTethering(type, wrappedCallback, showProvisioningUi);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception trying to start tethering.", e);
+ wrappedCallback.send(TETHER_ERROR_SERVICE_UNAVAIL, null);
+ }
+ }
+
+ /**
+ * Stops tethering for the given type. Also cancels any provisioning rechecks for that type if
+ * applicable.
+ *
+ * @param type The type of tethering to stop. Must be one of
+ * {@link ConnectivityManager.TETHERING_WIFI},
+ * {@link ConnectivityManager.TETHERING_USB}, or
+ * {@link ConnectivityManager.TETHERING_BLUETOOTH}.
+ * @hide
+ */
+ @SystemApi
+ public void stopTethering(int type) {
+ try {
+ mService.stopTethering(type);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception trying to stop tethering.", e);
+ }
+ }
+
+ /**
* Get the list of regular expressions that define any tetherable
* USB network interfaces. If USB tethering is not supported by the
* device, this list should be empty.
@@ -1949,6 +2116,8 @@
public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9;
/** {@hide} */
public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10;
+ /** {@hide} */
+ public static final int TETHER_ERROR_PROVISION_FAILED = 11;
/**
* Get a more detailed error code after a Tethering or Untethering
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 569468e..1a9c9ea 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -76,6 +76,10 @@
boolean isTetheringSupported();
+ void startTethering(int type, in ResultReceiver receiver, boolean showProvisioningUi);
+
+ void stopTethering(int type);
+
String[] getTetherableIfaces();
String[] getTetheredIfaces();
diff --git a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
index 66233b8..f5a2aae 100644
--- a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
@@ -29,12 +29,6 @@
public class TetherUtil {
- // Types of tethering.
- public static final int TETHERING_INVALID = -1;
- public static final int TETHERING_WIFI = 0;
- public static final int TETHERING_USB = 1;
- public static final int TETHERING_BLUETOOTH = 2;
-
// Extras used for communicating with the TetherService.
public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
@@ -43,14 +37,6 @@
* Tells the service to run a provision check now.
*/
public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
- /**
- * Enables wifi tethering if the provision check is successful. Used by
- * QS to enable tethering.
- */
- public static final String EXTRA_ENABLE_WIFI_TETHER = "extraEnableWifiTether";
-
- public static ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
- .getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
public static boolean setWifiTethering(boolean enable, Context context) {
final WifiManager wifiManager =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 41aeac9..5719f76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -20,8 +20,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
-import android.os.UserHandle;
import android.util.Log;
import com.android.settingslib.TetherUtil;
@@ -34,21 +34,18 @@
private static final String TAG = "HotspotController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final Intent TETHER_SERVICE_INTENT = new Intent()
- .putExtra(TetherUtil.EXTRA_ADD_TETHER_TYPE, TetherUtil.TETHERING_WIFI)
- .putExtra(TetherUtil.EXTRA_SET_ALARM, true)
- .putExtra(TetherUtil.EXTRA_RUN_PROVISION, true)
- .putExtra(TetherUtil.EXTRA_ENABLE_WIFI_TETHER, true)
- .setComponent(TetherUtil.TETHER_SERVICE);
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final Receiver mReceiver = new Receiver();
+ private final ConnectivityManager mConnectivityManager;
private final Context mContext;
private int mHotspotState;
public HotspotControllerImpl(Context context) {
mContext = context;
+ mConnectivityManager = (ConnectivityManager)context.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -72,6 +69,7 @@
return null;
}
+ @Override
public void addCallback(Callback callback) {
if (callback == null || mCallbacks.contains(callback)) return;
if (DEBUG) Log.d(TAG, "addCallback " + callback);
@@ -79,6 +77,7 @@
mReceiver.setListening(!mCallbacks.isEmpty());
}
+ @Override
public void removeCallback(Callback callback) {
if (callback == null) return;
if (DEBUG) Log.d(TAG, "removeCallback " + callback);
@@ -96,13 +95,24 @@
return TetherUtil.isTetheringSupported(mContext);
}
+ static final class OnStartTetheringCallback extends
+ ConnectivityManager.OnStartTetheringCallback {
+ @Override
+ public void onTetheringStarted() {}
+ @Override
+ public void onTetheringFailed() {
+ // TODO: Show error.
+ }
+ }
+
@Override
public void setHotspotEnabled(boolean enabled) {
- // Call provisioning app which is called when enabling Tethering from Settings
- if (enabled && TetherUtil.isProvisioningNeeded(mContext)) {
- mContext.startServiceAsUser(TETHER_SERVICE_INTENT, UserHandle.CURRENT);
+ if (enabled) {
+ OnStartTetheringCallback callback = new OnStartTetheringCallback();
+ mConnectivityManager.startTethering(
+ ConnectivityManager.TETHERING_WIFI, false, callback);
} else {
- TetherUtil.setWifiTethering(enabled, mContext);
+ mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 9927fd6..3c13630 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -87,6 +87,7 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -2686,6 +2687,21 @@
mTethering.getUpstreamIfaceTypes().length != 0);
}
+ public void startTethering(int type, ResultReceiver receiver,
+ boolean showProvisioningUi) {
+ ConnectivityManager.enforceTetherChangePermission(mContext);
+ if (!isTetheringSupported()) {
+ receiver.send(ConnectivityManager.TETHER_ERROR_UNSUPPORTED, null);
+ return;
+ }
+ mTethering.startTethering(type, receiver, showProvisioningUi);
+ }
+
+ public void stopTethering(int type) {
+ ConnectivityManager.enforceTetherChangePermission(mContext);
+ mTethering.stopTethering(type);
+ }
+
// Called when we lose the default network and have no replacement yet.
// This will automatically be cleared after X seconds or a new default network
// becomes CONNECTED, whichever happens first. The timer is started by the
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 3b43633..1d7e835 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -19,6 +19,10 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothProfile.ServiceListener;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -38,20 +42,24 @@
import android.net.NetworkRequest;
import android.net.NetworkUtils;
import android.net.RouteInfo;
+import android.net.wifi.WifiManager;
import android.os.Binder;
+import android.os.Bundle;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcel;
+import android.os.ResultReceiver;
+import android.os.SystemProperties;
import android.os.UserHandle;
+import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.IState;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.IoThread;
@@ -59,8 +67,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.net.InetAddress;
import java.net.Inet4Address;
+import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -109,6 +117,10 @@
private BroadcastReceiver mStateReceiver;
+ // {@link ComponentName} of the Service used to run tether provisioning.
+ private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
+ .getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
+
private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
private static final int USB_PREFIX_LENGTH = 24;
@@ -332,6 +344,206 @@
}
}
+ public void startTethering(int type, ResultReceiver receiver,
+ boolean showProvisioningUi) {
+ if (!isTetherProvisioningRequired()) {
+ enableTetheringInternal(type, true, receiver);
+ return;
+ }
+
+ if (showProvisioningUi) {
+ runUiTetherProvisioningAndEnable(type, receiver);
+ } else {
+ runSilentTetherProvisioningAndEnable(type, receiver);
+ }
+ }
+
+ public void stopTethering(int type) {
+ enableTetheringInternal(type, false, null);
+ if (isTetherProvisioningRequired()) {
+ cancelTetherProvisioningRechecks(type);
+ }
+ }
+
+ /**
+ * Check if the device requires a provisioning check in order to enable tethering.
+ *
+ * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
+ */
+ private boolean isTetherProvisioningRequired() {
+ String[] provisionApp = mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_mobile_hotspot_provision_app);
+ if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)
+ || provisionApp == null) {
+ return false;
+ }
+
+ // Check carrier config for entitlement checks
+ final CarrierConfigManager configManager = (CarrierConfigManager) mContext
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ boolean isEntitlementCheckRequired = configManager.getConfig().getBoolean(
+ CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
+
+ if (!isEntitlementCheckRequired) {
+ return false;
+ }
+ return (provisionApp.length == 2);
+ }
+
+ /**
+ * Enables or disables tethering for the given type. This should only be called once
+ * provisioning has succeeded or is not necessary. It will also schedule provisioning rechecks
+ * for the specified interface.
+ */
+ private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) {
+ boolean isProvisioningRequired = isTetherProvisioningRequired();
+ switch (type) {
+ case ConnectivityManager.TETHERING_WIFI:
+ final WifiManager wifiManager =
+ (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ if (wifiManager.setWifiApEnabled(null, enable)) {
+ sendTetherResult(receiver, ConnectivityManager.TETHER_ERROR_NO_ERROR);
+ if (enable && isProvisioningRequired) {
+ scheduleProvisioningRechecks(type);
+ }
+ } else{
+ sendTetherResult(receiver, ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
+ }
+ break;
+ case ConnectivityManager.TETHERING_USB:
+ int result = setUsbTethering(enable);
+ if (enable && isProvisioningRequired &&
+ result == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ scheduleProvisioningRechecks(type);
+ }
+ sendTetherResult(receiver, result);
+ break;
+ case ConnectivityManager.TETHERING_BLUETOOTH:
+ setBluetoothTethering(enable, receiver);
+ break;
+ default:
+ Log.w(TAG, "Invalid tether type.");
+ sendTetherResult(receiver, ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE);
+ }
+ }
+
+ private void sendTetherResult(ResultReceiver receiver, int result) {
+ if (receiver != null) {
+ receiver.send(result, null);
+ }
+ }
+
+ private void setBluetoothTethering(final boolean enable, final ResultReceiver receiver) {
+ final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter == null || !adapter.isEnabled()) {
+ Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: " +
+ (adapter == null));
+ sendTetherResult(receiver, ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL);
+ return;
+ }
+
+ adapter.getProfileProxy(mContext, new ServiceListener() {
+ @Override
+ public void onServiceDisconnected(int profile) { }
+
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ ((BluetoothPan) proxy).setBluetoothTethering(enable);
+ // TODO: Enabling bluetooth tethering can fail asynchronously here.
+ // We should figure out a way to bubble up that failure instead of sending success.
+ int result = ((BluetoothPan) proxy).isTetheringOn() ?
+ ConnectivityManager.TETHER_ERROR_NO_ERROR :
+ ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
+ sendTetherResult(receiver, result);
+ if (enable && isTetherProvisioningRequired()) {
+ scheduleProvisioningRechecks(ConnectivityManager.TETHERING_BLUETOOTH);
+ }
+ adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
+ }
+ }, BluetoothProfile.PAN);
+ }
+
+ private void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
+ // TODO: Fill in for real.
+ enableTetheringInternal(type, true, receiver);
+ }
+
+ /**
+ * Creates a proxy {@link ResultReceiver} which enables tethering if the provsioning result is
+ * successful before firing back up to the wrapped receiver.
+ *
+ * @param type The type of tethering being enabled.
+ * @param receiver A ResultReceiver which will be called back with an int resultCode.
+ * @return The proxy receiver.
+ */
+ private ResultReceiver getProxyReceiver(final int type, final ResultReceiver receiver) {
+ ResultReceiver rr = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ // If provisioning is successful, enable tethering, otherwise just send the error.
+ if (resultCode == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ enableTetheringInternal(type, true, receiver);
+ } else {
+ sendTetherResult(receiver, resultCode);
+ }
+ }
+ };
+
+ // The following is necessary to avoid unmarshalling issues when sending the receiver
+ // across proccesses.
+ Parcel parcel = Parcel.obtain();
+ rr.writeToParcel(parcel,0);
+ parcel.setDataPosition(0);
+ ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+ return receiverForSending;
+ }
+
+ private void scheduleProvisioningRechecks(int type) {
+ Intent intent = new Intent();
+ intent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(ConnectivityManager.EXTRA_SET_ALARM, true);
+ intent.setComponent(TETHER_SERVICE);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
+ ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
+ sendSilentTetherProvisionIntent(type, proxyReceiver);
+ }
+
+ private void sendSilentTetherProvisionIntent(int type, ResultReceiver receiver) {
+ Intent intent = new Intent();
+ intent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(ConnectivityManager.EXTRA_RUN_PROVISION, true);
+ intent.putExtra(ConnectivityManager.EXTRA_PROVISION_CALLBACK, receiver);
+ intent.setComponent(TETHER_SERVICE);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void cancelTetherProvisioningRechecks(int type) {
+ if (getConnectivityManager().isTetheringSupported()) {
+ Intent intent = new Intent();
+ intent.putExtra(ConnectivityManager.EXTRA_REM_TETHER_TYPE, type);
+ intent.setComponent(TETHER_SERVICE);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
public int tether(String iface) {
if (DBG) Log.d(TAG, "Tethering " + iface);
TetherInterfaceSM sm = null;
@@ -615,13 +827,23 @@
synchronized (mPublicSync) {
if (enable) {
if (mRndisEnabled) {
- tetherUsb(true);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ tetherUsb(true);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
} else {
mUsbTetherRequested = true;
usbManager.setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS);
}
} else {
- tetherUsb(false);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ tetherUsb(false);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
if (mRndisEnabled) {
usbManager.setCurrentFunction(null);
}
@@ -1407,15 +1629,6 @@
private final AtomicInteger mSimBcastGenerationNumber = new AtomicInteger(0);
private SimChangeBroadcastReceiver mBroadcastReceiver = null;
- // keep consts in sync with packages/apps/Settings TetherSettings.java
- private static final int WIFI_TETHERING = 0;
- private static final int USB_TETHERING = 1;
- private static final int BLUETOOTH_TETHERING = 2;
-
- // keep consts in sync with packages/apps/Settings TetherService.java
- private static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
- private static final String EXTRA_RUN_PROVISION = "extraRunProvision";
-
private void startListeningForSimChanges() {
if (DBG) Log.d(TAG, "startListeningForSimChanges");
if (mBroadcastReceiver == null) {
@@ -1472,8 +1685,6 @@
try {
if (mContext.getResources().getString(com.android.internal.R.string.
config_mobile_hotspot_provision_app_no_ui).isEmpty() == false) {
- final String tetherService = mContext.getResources().getString(
- com.android.internal.R.string.config_wifi_tether_enable);
ArrayList<Integer> tethered = new ArrayList<Integer>();
synchronized (mPublicSync) {
Set ifaces = mIfaces.keySet();
@@ -1481,21 +1692,25 @@
TetherInterfaceSM sm = mIfaces.get(iface);
if (sm != null && sm.isTethered()) {
if (isUsb((String)iface)) {
- tethered.add(new Integer(USB_TETHERING));
+ tethered.add(new Integer(
+ ConnectivityManager.TETHERING_USB));
} else if (isWifi((String)iface)) {
- tethered.add(new Integer(WIFI_TETHERING));
+ tethered.add(new Integer(
+ ConnectivityManager.TETHERING_WIFI));
} else if (isBluetooth((String)iface)) {
- tethered.add(new Integer(BLUETOOTH_TETHERING));
+ tethered.add(new Integer(
+ ConnectivityManager.TETHERING_BLUETOOTH));
}
}
}
}
for (int tetherType : tethered) {
Intent startProvIntent = new Intent();
- startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
- startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
- startProvIntent.setComponent(
- ComponentName.unflattenFromString(tetherService));
+ startProvIntent.putExtra(
+ ConnectivityManager.EXTRA_ADD_TETHER_TYPE, tetherType);
+ startProvIntent.putExtra(
+ ConnectivityManager.EXTRA_RUN_PROVISION, true);
+ startProvIntent.setComponent(TETHER_SERVICE);
mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
}
Log.d(TAG, "re-evaluate provisioning");