diff options
| author | 2016-01-26 03:59:51 +0000 | |
|---|---|---|
| committer | 2016-01-26 03:59:51 +0000 | |
| commit | 0301cd95231a5c59d4f97e24d11a0ea5db909c4f (patch) | |
| tree | 91739e11ebbafc79ff346a2b6e4e283d72c57d9e | |
| parent | 50cd6361d7cb50bdc0ee199f42307885abc65f0b (diff) | |
| parent | 36c7aa03255d91cfa0808323ac475ad02d161d7d (diff) | |
Merge "Expose a simple tethering API which includes provision checks."
7 files changed, 462 insertions, 49 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index e4f42c05843f..dbaecd7aa784 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -24485,6 +24485,7 @@ package android.net { 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 @@ package android.net { 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 @@ package android.net { 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 @@ package android.net { 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 08c0c094a473..7cb086fa717b 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.Intent; 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.Message; 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 class ConnectivityManager { 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 @@ public class ConnectivityManager { * 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 @@ public class ConnectivityManager { * 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 @@ public class ConnectivityManager { * * {@hide} */ + @SystemApi public boolean isTetheringSupported() { try { return mService.isTetheringSupported(); @@ -1843,6 +1922,94 @@ public class ConnectivityManager { } /** + * 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 class ConnectivityManager { 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 569468e19687..1a9c9ea4eb8d 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -76,6 +76,10 @@ interface IConnectivityManager 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 66233b82b8dc..f5a2aaec97d7 100644 --- a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java +++ b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java @@ -29,12 +29,6 @@ import android.telephony.CarrierConfigManager; 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 @@ public class TetherUtil { * 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 41aeac9ef3f0..5719f76a7480 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.BroadcastReceiver; 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 @@ public class HotspotControllerImpl implements HotspotController { 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 @@ public class HotspotControllerImpl implements HotspotController { 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 @@ public class HotspotControllerImpl implements HotspotController { 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 @@ public class HotspotControllerImpl implements HotspotController { 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 9927fd6c5546..3c13630dd8ba 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.ParcelFileDescriptor; 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 3b43633cfc37..1d7e835888fe 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 @@ package com.android.server.connectivity; 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.NetworkInfo; 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 com.android.server.net.BaseNetworkObserver; 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 @@ public class Tethering extends BaseNetworkObserver { 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 class Tethering extends BaseNetworkObserver { } } + 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 @@ public class Tethering extends BaseNetworkObserver { 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 @@ public class Tethering extends BaseNetworkObserver { 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 @@ public class Tethering extends BaseNetworkObserver { 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 @@ public class Tethering extends BaseNetworkObserver { 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"); |