Merge "Allows the caller to specify configuration by TetheringRequest"
diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp
index a8d1239..b0ef153 100644
--- a/Tethering/common/TetheringLib/Android.bp
+++ b/Tethering/common/TetheringLib/Android.bp
@@ -70,6 +70,7 @@
"src/android/net/ITetheringConnector.aidl",
"src/android/net/TetheringCallbackStartedParcel.aidl",
"src/android/net/TetheringConfigurationParcel.aidl",
+ "src/android/net/TetheringRequestParcel.aidl",
"src/android/net/TetherStatesParcel.aidl",
],
path: "src"
diff --git a/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl b/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
index d30c399..5febe73 100644
--- a/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
+++ b/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
@@ -17,6 +17,7 @@
import android.net.IIntResultListener;
import android.net.ITetheringEventCallback;
+import android.net.TetheringRequestParcel;
import android.os.ResultReceiver;
/** @hide */
@@ -27,8 +28,8 @@
void setUsbTethering(boolean enable, String callerPkg, IIntResultListener receiver);
- void startTethering(int type, in ResultReceiver receiver, boolean showProvisioningUi,
- String callerPkg);
+ void startTethering(in TetheringRequestParcel request, String callerPkg,
+ IIntResultListener receiver);
void stopTethering(int type, String callerPkg, IIntResultListener receiver);
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index e1b9c16..58bc4e7 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -15,6 +15,7 @@
*/
package android.net;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -326,27 +327,171 @@
}
/**
+ * Use with {@link #startTethering} to specify additional parameters when starting tethering.
+ */
+ public static class TetheringRequest {
+ /** A configuration set for TetheringRequest. */
+ private final TetheringRequestParcel mRequestParcel;
+
+ private TetheringRequest(final TetheringRequestParcel request) {
+ mRequestParcel = request;
+ }
+
+ /** Builder used to create TetheringRequest. */
+ public static class Builder {
+ private final TetheringRequestParcel mBuilderParcel;
+
+ /** Default constructor of Builder. */
+ public Builder(final int type) {
+ mBuilderParcel = new TetheringRequestParcel();
+ mBuilderParcel.tetheringType = type;
+ mBuilderParcel.localIPv4Address = null;
+ mBuilderParcel.exemptFromEntitlementCheck = false;
+ mBuilderParcel.showProvisioningUi = true;
+ }
+
+ /**
+ * Configure tethering with static IPv4 assignment (with DHCP disabled).
+ *
+ * @param localIPv4Address The preferred local IPv4 address to use.
+ */
+ @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
+ @NonNull
+ public Builder useStaticIpv4Addresses(@NonNull final LinkAddress localIPv4Address) {
+ mBuilderParcel.localIPv4Address = localIPv4Address;
+ return this;
+ }
+
+ /** Start tethering without entitlement checks. */
+ @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
+ @NonNull
+ public Builder setExemptFromEntitlementCheck(boolean exempt) {
+ mBuilderParcel.exemptFromEntitlementCheck = exempt;
+ return this;
+ }
+
+ /** Start tethering without showing the provisioning UI. */
+ @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
+ @NonNull
+ public Builder setSilentProvisioning(boolean silent) {
+ mBuilderParcel.showProvisioningUi = silent;
+ return this;
+ }
+
+ /** Build {@link TetheringRequest] with the currently set configuration. */
+ @NonNull
+ public TetheringRequest build() {
+ return new TetheringRequest(mBuilderParcel);
+ }
+ }
+
+ /**
+ * Get a TetheringRequestParcel from the configuration
+ * @hide
+ */
+ public TetheringRequestParcel getParcel() {
+ return mRequestParcel;
+ }
+
+ /** String of TetheringRequest detail. */
+ public String toString() {
+ return "TetheringRequest [ type= " + mRequestParcel.tetheringType
+ + ", localIPv4Address= " + mRequestParcel.localIPv4Address
+ + ", exemptFromEntitlementCheck= "
+ + mRequestParcel.exemptFromEntitlementCheck + ", showProvisioningUi= "
+ + mRequestParcel.showProvisioningUi + " ]";
+ }
+ }
+
+ /**
+ * Callback for use with {@link #startTethering} to find out whether tethering succeeded.
+ */
+ public abstract static class StartTetheringCallback {
+ /**
+ * Called when tethering has been successfully started.
+ */
+ public void onTetheringStarted() {}
+
+ /**
+ * Called when starting tethering failed.
+ *
+ * @param resultCode One of the {@code TETHER_ERROR_*} constants.
+ */
+ public void onTetheringFailed(final int resultCode) {}
+ }
+
+ /**
* Starts tethering and runs tether provisioning for the given type if needed. If provisioning
* fails, stopTethering will be called automatically.
- * @hide
+ *
+ * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will
+ * fail if a tethering entitlement check is required.
+ *
+ * @param request a {@link TetheringRequest} which can specify the preferred configuration.
+ * @param executor {@link Executor} to specify the thread upon which the callback of
+ * TetheringRequest will be invoked.
+ * @param callback A callback that will be called to indicate the success status of the
+ * tethering start request.
*/
- // TODO: improve the usage of ResultReceiver, b/145096122
- public void startTethering(final int type, @NonNull final ResultReceiver receiver,
- final boolean showProvisioningUi) {
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.TETHER_PRIVILEGED,
+ android.Manifest.permission.WRITE_SETTINGS
+ })
+ public void startTethering(@NonNull final TetheringRequest request,
+ @NonNull final Executor executor, @NonNull final StartTetheringCallback callback) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "startTethering caller:" + callerPkg);
+ final IIntResultListener listener = new IIntResultListener.Stub() {
+ @Override
+ public void onResult(final int resultCode) {
+ executor.execute(() -> {
+ if (resultCode == TETHER_ERROR_NO_ERROR) {
+ callback.onTetheringStarted();
+ } else {
+ callback.onTetheringFailed(resultCode);
+ }
+ });
+ }
+ };
try {
- mConnector.startTethering(type, receiver, showProvisioningUi, callerPkg);
+ mConnector.startTethering(request.getParcel(), callerPkg, listener);
} catch (RemoteException e) {
throw new IllegalStateException(e);
}
}
/**
+ * Starts tethering and runs tether provisioning for the given type if needed. If provisioning
+ * fails, stopTethering will be called automatically.
+ *
+ * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will
+ * fail if a tethering entitlement check is required.
+ *
+ * @param type The tethering type, on of the {@code TetheringManager#TETHERING_*} constants.
+ * @param executor {@link Executor} to specify the thread upon which the callback of
+ * TetheringRequest will be invoked.
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.TETHER_PRIVILEGED,
+ android.Manifest.permission.WRITE_SETTINGS
+ })
+ public void startTethering(int type, @NonNull final Executor executor,
+ @NonNull final StartTetheringCallback callback) {
+ startTethering(new TetheringRequest.Builder(type).build(), executor, callback);
+ }
+
+ /**
* Stops tethering for the given type. Also cancels any provisioning rechecks for that type if
* applicable.
+ *
+ * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will
+ * fail if a tethering entitlement check is required.
*/
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.TETHER_PRIVILEGED,
+ android.Manifest.permission.WRITE_SETTINGS
+ })
public void stopTethering(final int type) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "stopTethering caller:" + callerPkg);
@@ -386,6 +531,9 @@
* {@link #TETHER_ERROR_ENTITLEMENT_UNKNOWN} will be returned. If {@code showEntitlementUi} is
* true, entitlement will be run.
*
+ * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will
+ * fail if a tethering entitlement check is required.
+ *
* @param type the downstream type of tethering. Must be one of {@code #TETHERING_*} constants.
* @param showEntitlementUi a boolean indicating whether to run UI-based entitlement check.
* @param executor the executor on which callback will be invoked.
@@ -393,7 +541,10 @@
* notify the caller of the result of entitlement check. The listener may be called zero
* or one time.
*/
- @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.TETHER_PRIVILEGED,
+ android.Manifest.permission.WRITE_SETTINGS
+ })
public void requestLatestTetheringEntitlementResult(int type, boolean showEntitlementUi,
@NonNull Executor executor,
@NonNull final OnTetheringEntitlementResultListener listener) {
@@ -562,6 +713,7 @@
* @param executor the executor on which callback will be invoked.
* @param callback the callback to be called when tethering has change events.
*/
+ @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE)
public void registerTetheringEventCallback(@NonNull Executor executor,
@NonNull TetheringEventCallback callback) {
final String callerPkg = mContext.getOpPackageName();
@@ -669,6 +821,10 @@
*
* @param callback previously registered callback.
*/
+ @RequiresPermission(anyOf = {
+ Manifest.permission.TETHER_PRIVILEGED,
+ Manifest.permission.ACCESS_NETWORK_STATE
+ })
public void unregisterTetheringEventCallback(@NonNull final TetheringEventCallback callback) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "unregisterTetheringEventCallback caller:" + callerPkg);
@@ -833,7 +989,14 @@
/**
* Stop all active tethering.
+ *
+ * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will
+ * fail if a tethering entitlement check is required.
*/
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.TETHER_PRIVILEGED,
+ android.Manifest.permission.WRITE_SETTINGS
+ })
public void stopAllTethering() {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "stopAllTethering caller:" + callerPkg);
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl b/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl
new file mode 100644
index 0000000..bf19d85
--- /dev/null
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.LinkAddress;
+
+/**
+ * Configuration details for requesting tethering.
+ * @hide
+ */
+parcelable TetheringRequestParcel {
+ int tetheringType;
+ LinkAddress localIPv4Address;
+ boolean exemptFromEntitlementCheck;
+ boolean showProvisioningUi;
+}
diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index 5370145..c47f2d6 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -66,6 +66,7 @@
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
+import android.net.IIntResultListener;
import android.net.INetd;
import android.net.ITetheringEventCallback;
import android.net.IpPrefix;
@@ -76,6 +77,7 @@
import android.net.TetherStatesParcel;
import android.net.TetheringCallbackStartedParcel;
import android.net.TetheringConfigurationParcel;
+import android.net.TetheringRequestParcel;
import android.net.ip.IpServer;
import android.net.shared.NetdUtils;
import android.net.util.BaseNetdUnsolicitedEventListener;
@@ -424,9 +426,10 @@
}
}
- void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {
- mEntitlementMgr.startProvisioningIfNeeded(type, showProvisioningUi);
- enableTetheringInternal(type, true /* enabled */, receiver);
+ void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) {
+ mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType,
+ request.showProvisioningUi);
+ enableTetheringInternal(request.tetheringType, true /* enabled */, listener);
}
void stopTethering(int type) {
@@ -438,29 +441,32 @@
* Enables or disables tethering for the given type. If provisioning is required, it will
* schedule provisioning rechecks for the specified interface.
*/
- private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) {
+ private void enableTetheringInternal(int type, boolean enable,
+ final IIntResultListener listener) {
int result;
switch (type) {
case TETHERING_WIFI:
result = setWifiTethering(enable);
- sendTetherResult(receiver, result);
+ sendTetherResult(listener, result);
break;
case TETHERING_USB:
result = setUsbTethering(enable);
- sendTetherResult(receiver, result);
+ sendTetherResult(listener, result);
break;
case TETHERING_BLUETOOTH:
- setBluetoothTethering(enable, receiver);
+ setBluetoothTethering(enable, listener);
break;
default:
Log.w(TAG, "Invalid tether type.");
- sendTetherResult(receiver, TETHER_ERROR_UNKNOWN_IFACE);
+ sendTetherResult(listener, TETHER_ERROR_UNKNOWN_IFACE);
}
}
- private void sendTetherResult(ResultReceiver receiver, int result) {
- if (receiver != null) {
- receiver.send(result, null);
+ private void sendTetherResult(final IIntResultListener listener, int result) {
+ if (listener != null) {
+ try {
+ listener.onResult(result);
+ } catch (RemoteException e) { }
}
}
@@ -486,12 +492,12 @@
return TETHER_ERROR_MASTER_ERROR;
}
- private void setBluetoothTethering(final boolean enable, final ResultReceiver receiver) {
+ private void setBluetoothTethering(final boolean enable, final IIntResultListener listener) {
final BluetoothAdapter adapter = mDeps.getBluetoothAdapter();
if (adapter == null || !adapter.isEnabled()) {
Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: "
+ (adapter == null));
- sendTetherResult(receiver, TETHER_ERROR_SERVICE_UNAVAIL);
+ sendTetherResult(listener, TETHER_ERROR_SERVICE_UNAVAIL);
return;
}
@@ -520,7 +526,7 @@
final int result = (((BluetoothPan) proxy).isTetheringOn() == enable)
? TETHER_ERROR_NO_ERROR
: TETHER_ERROR_MASTER_ERROR;
- sendTetherResult(receiver, result);
+ sendTetherResult(listener, result);
adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
}
}, BluetoothProfile.PAN);
diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
index cb7d392..7dc5c5f 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -33,6 +33,7 @@
import android.net.ITetheringEventCallback;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.net.TetheringRequestParcel;
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.ip.IpServer;
@@ -143,11 +144,11 @@
}
@Override
- public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi,
- String callerPkg) {
- if (checkAndNotifyCommonError(callerPkg, receiver)) return;
+ public void startTethering(TetheringRequestParcel request, String callerPkg,
+ IIntResultListener listener) {
+ if (checkAndNotifyCommonError(callerPkg, listener)) return;
- mTethering.startTethering(type, receiver, showProvisioningUi);
+ mTethering.startTethering(request, listener);
}
@Override
diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index 9f0d876..6fc0c65 100644
--- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -88,6 +88,7 @@
import android.net.TetherStatesParcel;
import android.net.TetheringCallbackStartedParcel;
import android.net.TetheringConfigurationParcel;
+import android.net.TetheringRequestParcel;
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServer;
@@ -468,6 +469,16 @@
return new Tethering(mTetheringDependencies);
}
+ private TetheringRequestParcel createTetheringRquestParcel(final int type) {
+ final TetheringRequestParcel request = new TetheringRequestParcel();
+ request.tetheringType = type;
+ request.localIPv4Address = null;
+ request.exemptFromEntitlementCheck = false;
+ request.showProvisioningUi = false;
+
+ return request;
+ }
+
@After
public void tearDown() {
mServiceContext.unregisterReceiver(mBroadcastReceiver);
@@ -573,7 +584,7 @@
.thenReturn(upstreamState);
// Emulate pressing the USB tethering button in Settings UI.
- mTethering.startTethering(TETHERING_USB, null, false);
+ mTethering.startTethering(createTetheringRquestParcel(TETHERING_USB), null);
mLooper.dispatchAll();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
@@ -819,7 +830,7 @@
when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(TETHERING_WIFI, null, false);
+ mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startTetheredHotspot(null);
verifyNoMoreInteractions(mWifiManager);
@@ -846,7 +857,7 @@
when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(TETHERING_WIFI, null, false);
+ mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startTetheredHotspot(null);
verifyNoMoreInteractions(mWifiManager);
@@ -923,7 +934,7 @@
doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(TETHERING_WIFI, null, false);
+ mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startTetheredHotspot(null);
verifyNoMoreInteractions(mWifiManager);
@@ -1188,7 +1199,7 @@
tetherState = callback.pollTetherStatesChanged();
assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME});
- mTethering.startTethering(TETHERING_WIFI, null, false);
+ mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null);
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
mLooper.dispatchAll();
tetherState = callback.pollTetherStatesChanged();