Allows the caller to specify configuration by TetheringRequest
This is initial work to allow caller to pass their prefered
configuration to start tethering. Caller may able to specify the
downstream interface ipv4 address with dhcp server disabled for
static IP configuration, or able to exempt entitlement check if
they have permission in follow up CL.
Bug: 141256482
Test: -atest TetheringTest
-ON/OFF wifi tethering
Change-Id: Ic7c3a33195bbd7e72f9b8e73fa148be476b87bf3
Merged-In: Ic7c3a33195bbd7e72f9b8e73fa148be476b87bf3
This commit is contained in:
committed by
Lorenzo Colitti
parent
249752d8a3
commit
f6761bdd0c
@@ -70,6 +70,7 @@ filegroup {
|
||||
"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"
|
||||
|
||||
@@ -17,6 +17,7 @@ package android.net;
|
||||
|
||||
import android.net.IIntResultListener;
|
||||
import android.net.ITetheringEventCallback;
|
||||
import android.net.TetheringRequestParcel;
|
||||
import android.os.ResultReceiver;
|
||||
|
||||
/** @hide */
|
||||
@@ -27,8 +28,8 @@ oneway interface ITetheringConnector {
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -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 @@ public class TetheringManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts tethering and runs tether provisioning for the given type if needed. If provisioning
|
||||
* fails, stopTethering will be called automatically.
|
||||
* 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
|
||||
*/
|
||||
// TODO: improve the usage of ResultReceiver, b/145096122
|
||||
public void startTethering(final int type, @NonNull final ResultReceiver receiver,
|
||||
final boolean showProvisioningUi) {
|
||||
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.
|
||||
*
|
||||
* <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.
|
||||
*/
|
||||
@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 @@ public class TetheringManager {
|
||||
* {@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 @@ public class TetheringManager {
|
||||
* 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 @@ public class TetheringManager {
|
||||
* @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 @@ public class TetheringManager {
|
||||
*
|
||||
* @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 @@ public class TetheringManager {
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -66,6 +66,7 @@ import android.content.IntentFilter;
|
||||
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.NetworkInfo;
|
||||
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 @@ public class Tethering {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public class Tethering {
|
||||
* 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 @@ public class Tethering {
|
||||
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 @@ public class Tethering {
|
||||
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);
|
||||
|
||||
@@ -33,6 +33,7 @@ import android.net.ITetheringConnector;
|
||||
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 @@ public class TetheringService extends Service {
|
||||
}
|
||||
|
||||
@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
|
||||
|
||||
@@ -88,6 +88,7 @@ import android.net.RouteInfo;
|
||||
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 @@ public class TetheringTest {
|
||||
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 @@ public class TetheringTest {
|
||||
.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 @@ public class TetheringTest {
|
||||
when(mWifiManager.startSoftAp(any(WifiConfiguration.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)).startSoftAp(null);
|
||||
verifyNoMoreInteractions(mWifiManager);
|
||||
@@ -846,7 +857,7 @@ public class TetheringTest {
|
||||
when(mWifiManager.startSoftAp(any(WifiConfiguration.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)).startSoftAp(null);
|
||||
verifyNoMoreInteractions(mWifiManager);
|
||||
@@ -923,7 +934,7 @@ public class TetheringTest {
|
||||
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)).startSoftAp(null);
|
||||
verifyNoMoreInteractions(mWifiManager);
|
||||
@@ -1188,7 +1199,7 @@ public class TetheringTest {
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user