[Revert^2] Reject TetheringRequests that fuzzy-match existing ones am: 8b29d287f5
Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/3543706 Change-Id: I6ac631f6cb393f406eb7c89036062480e29e40c1 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
commit
f4191218f8
7 changed files with 733 additions and 116 deletions
|
|
@ -37,6 +37,7 @@ import android.os.Parcelable;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ResultReceiver;
|
import android.os.ResultReceiver;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
@ -1258,6 +1259,44 @@ public class TetheringManager {
|
||||||
return sj.toString();
|
return sj.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UnflaggedApi")
|
||||||
|
private static boolean supportsInterfaceName(int tetheringType) {
|
||||||
|
// TODO: Check the interface name for TETHERING_WIFI and TETHERING_WIFI_P2P once
|
||||||
|
// they're actually used.
|
||||||
|
// Suppress lint for TETHERING_VIRTUAL since this method is only used internally.
|
||||||
|
return tetheringType == TETHERING_VIRTUAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean supportsConcurrentConnectivityScopes(int tetheringType) {
|
||||||
|
// Currently, only WIFI supports simultaneous local and global connectivity.
|
||||||
|
// This can't happen for REQUEST_TYPE_EXPLICIT requests, because
|
||||||
|
// TetheringRequest.Builder will not allow building an explicit TetheringRequest
|
||||||
|
// with TETHERING_WIFI and CONNECTIVITY_SCOPE_LOCAL, but when local-only hotspot
|
||||||
|
// is running, there is a REQUEST_TYPE_IMPLICIT request in the serving request list.
|
||||||
|
return tetheringType == TETHERING_WIFI;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the other TetheringRequest "fuzzy" matches this one. This is used
|
||||||
|
* internally to match tracked requests with external requests from API calls, and to reject
|
||||||
|
* additional requests that the link layer has no capacity for.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public boolean fuzzyMatches(final TetheringRequest other) {
|
||||||
|
if (other == null) return false;
|
||||||
|
final int type = getTetheringType();
|
||||||
|
if (type != other.getTetheringType()) return false;
|
||||||
|
if (supportsInterfaceName(type)
|
||||||
|
&& !TextUtils.equals(getInterfaceName(), other.getInterfaceName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (supportsConcurrentConnectivityScopes(type)
|
||||||
|
&& getConnectivityScope() != other.getConnectivityScope()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
|
|
@ -1279,6 +1318,8 @@ public class TetheringManager {
|
||||||
public boolean equalsIgnoreUidPackage(TetheringRequest otherRequest) {
|
public boolean equalsIgnoreUidPackage(TetheringRequest otherRequest) {
|
||||||
TetheringRequestParcel parcel = getParcel();
|
TetheringRequestParcel parcel = getParcel();
|
||||||
TetheringRequestParcel otherParcel = otherRequest.getParcel();
|
TetheringRequestParcel otherParcel = otherRequest.getParcel();
|
||||||
|
// Note: Changes here should also be reflected in fuzzyMatches(TetheringRequest) when
|
||||||
|
// appropriate.
|
||||||
return parcel.requestType == otherParcel.requestType
|
return parcel.requestType == otherParcel.requestType
|
||||||
&& parcel.tetheringType == otherParcel.tetheringType
|
&& parcel.tetheringType == otherParcel.tetheringType
|
||||||
&& Objects.equals(parcel.localIPv4Address, otherParcel.localIPv4Address)
|
&& Objects.equals(parcel.localIPv4Address, otherParcel.localIPv4Address)
|
||||||
|
|
|
||||||
|
|
@ -415,12 +415,6 @@ public class IpServer extends StateMachineShim {
|
||||||
return mIpv4PrefixRequest;
|
return mIpv4PrefixRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The TetheringRequest the IpServer started with. */
|
|
||||||
@Nullable
|
|
||||||
public TetheringRequest getTetheringRequest() {
|
|
||||||
return mTetheringRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the latest list of DHCP leases that was reported. Must be called on the IpServer looper
|
* Get the latest list of DHCP leases that was reported. Must be called on the IpServer looper
|
||||||
* thread.
|
* thread.
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ package com.android.networkstack.tethering;
|
||||||
import static com.android.networkstack.tethering.util.TetheringUtils.createPlaceholderRequest;
|
import static com.android.networkstack.tethering.util.TetheringUtils.createPlaceholderRequest;
|
||||||
|
|
||||||
import android.net.TetheringManager.TetheringRequest;
|
import android.net.TetheringManager.TetheringRequest;
|
||||||
|
import android.net.ip.IpServer;
|
||||||
|
import android.util.ArrayMap;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
@ -28,6 +30,7 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to keep track of tethering requests.
|
* Helper class to keep track of tethering requests.
|
||||||
|
|
@ -36,29 +39,17 @@ import java.util.List;
|
||||||
* layer to start.
|
* layer to start.
|
||||||
* 2) When the link layer is up, use {@link #getOrCreatePendingRequest(int)} to get a request to
|
* 2) When the link layer is up, use {@link #getOrCreatePendingRequest(int)} to get a request to
|
||||||
* start IP serving with.
|
* start IP serving with.
|
||||||
* 3) Remove all pending requests with {@link #removeAllPendingRequests(int)}.
|
* 3) Remove pending request with {@link #removePendingRequest(TetheringRequest)}.
|
||||||
* Note: This class is not thread-safe.
|
* Note: This class is not thread-safe.
|
||||||
* TODO: Add the pending IIntResultListeners at the same time as the pending requests, and
|
|
||||||
* call them when we get the tether result.
|
|
||||||
* TODO: Add support for multiple Bluetooth requests before the PAN service connects instead of
|
|
||||||
* using a separate mPendingPanRequestListeners.
|
|
||||||
* TODO: Add support for fuzzy-matched requests.
|
|
||||||
*/
|
*/
|
||||||
public class RequestTracker {
|
public class RequestTracker {
|
||||||
private static final String TAG = RequestTracker.class.getSimpleName();
|
private static final String TAG = RequestTracker.class.getSimpleName();
|
||||||
|
|
||||||
private class PendingRequest {
|
@NonNull
|
||||||
@NonNull
|
private final boolean mUseFuzzyMatching;
|
||||||
private final TetheringRequest mTetheringRequest;
|
|
||||||
|
|
||||||
private PendingRequest(@NonNull TetheringRequest tetheringRequest) {
|
public RequestTracker(boolean useFuzzyMatching) {
|
||||||
mTetheringRequest = tetheringRequest;
|
mUseFuzzyMatching = useFuzzyMatching;
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
TetheringRequest getTetheringRequest() {
|
|
||||||
return mTetheringRequest;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum AddResult {
|
public enum AddResult {
|
||||||
|
|
@ -69,24 +60,51 @@ public class RequestTracker {
|
||||||
/**
|
/**
|
||||||
* Failure indicating that the request could not be added due to a request of the same type
|
* Failure indicating that the request could not be added due to a request of the same type
|
||||||
* with conflicting parameters already pending. If so, we must stop tethering for the
|
* with conflicting parameters already pending. If so, we must stop tethering for the
|
||||||
* pending request before trying to add the result again.
|
* pending request before trying to add the result again. Only returned on V-.
|
||||||
*/
|
*/
|
||||||
FAILURE_CONFLICTING_PENDING_REQUEST
|
FAILURE_DUPLICATE_REQUEST_RESTART,
|
||||||
|
/**
|
||||||
|
* Failure indicating that the request could not be added due to a fuzzy-matched request
|
||||||
|
* already pending or serving. Only returned on B+.
|
||||||
|
*/
|
||||||
|
FAILURE_DUPLICATE_REQUEST_ERROR,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of pending requests added by {@link #addPendingRequest(TetheringRequest)}. There can be
|
* List of pending requests added by {@link #addPendingRequest(TetheringRequest)}
|
||||||
* only one per type, since we remove every request of the same type when we add a request.
|
* There can be only one per type, since we remove every request of the
|
||||||
|
* same type when we add a request.
|
||||||
*/
|
*/
|
||||||
private final List<PendingRequest> mPendingRequests = new ArrayList<>();
|
private final List<TetheringRequest> mPendingRequests = new ArrayList<>();
|
||||||
|
/**
|
||||||
|
* List of serving requests added by
|
||||||
|
* {@link #promoteRequestToServing(IpServer, TetheringRequest)}.
|
||||||
|
*/
|
||||||
|
private final Map<IpServer, TetheringRequest> mServingRequests = new ArrayMap<>();
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
List<TetheringRequest> getPendingTetheringRequests() {
|
List<TetheringRequest> getPendingTetheringRequests() {
|
||||||
List<TetheringRequest> requests = new ArrayList<>();
|
return new ArrayList<>(mPendingRequests);
|
||||||
for (PendingRequest pendingRequest : mPendingRequests) {
|
}
|
||||||
requests.add(pendingRequest.getTetheringRequest());
|
|
||||||
|
/**
|
||||||
|
* Adds a pending request or fails with FAILURE_CONFLICTING_REQUEST_FAIL if the request
|
||||||
|
* fuzzy-matches an existing request (either pending or serving).
|
||||||
|
*/
|
||||||
|
public AddResult addPendingRequestFuzzyMatched(@NonNull final TetheringRequest newRequest) {
|
||||||
|
List<TetheringRequest> existingRequests = new ArrayList<>();
|
||||||
|
existingRequests.addAll(mServingRequests.values());
|
||||||
|
existingRequests.addAll(mPendingRequests);
|
||||||
|
for (TetheringRequest request : existingRequests) {
|
||||||
|
if (request.fuzzyMatches(newRequest)) {
|
||||||
|
Log.i(TAG, "Cannot add pending request due to existing fuzzy-matched "
|
||||||
|
+ "request: " + request);
|
||||||
|
return AddResult.FAILURE_DUPLICATE_REQUEST_ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return requests;
|
|
||||||
|
mPendingRequests.add(newRequest);
|
||||||
|
return AddResult.SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -95,9 +113,12 @@ public class RequestTracker {
|
||||||
* layer comes up. The result of the add operation will be returned as an AddResult code.
|
* layer comes up. The result of the add operation will be returned as an AddResult code.
|
||||||
*/
|
*/
|
||||||
public AddResult addPendingRequest(@NonNull final TetheringRequest newRequest) {
|
public AddResult addPendingRequest(@NonNull final TetheringRequest newRequest) {
|
||||||
|
if (mUseFuzzyMatching) {
|
||||||
|
return addPendingRequestFuzzyMatched(newRequest);
|
||||||
|
}
|
||||||
|
|
||||||
// Check the existing requests to see if it is OK to add the new request.
|
// Check the existing requests to see if it is OK to add the new request.
|
||||||
for (PendingRequest request : mPendingRequests) {
|
for (TetheringRequest existingRequest : mPendingRequests) {
|
||||||
TetheringRequest existingRequest = request.getTetheringRequest();
|
|
||||||
if (existingRequest.getTetheringType() != newRequest.getTetheringType()) {
|
if (existingRequest.getTetheringType() != newRequest.getTetheringType()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +126,7 @@ public class RequestTracker {
|
||||||
// Can't add request if there's a request of the same type with different
|
// Can't add request if there's a request of the same type with different
|
||||||
// parameters.
|
// parameters.
|
||||||
if (!existingRequest.equalsIgnoreUidPackage(newRequest)) {
|
if (!existingRequest.equalsIgnoreUidPackage(newRequest)) {
|
||||||
return AddResult.FAILURE_CONFLICTING_PENDING_REQUEST;
|
return AddResult.FAILURE_DUPLICATE_REQUEST_RESTART;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,7 +134,7 @@ public class RequestTracker {
|
||||||
// conflicting parameters above, so these would have been equivalent anyway (except for
|
// conflicting parameters above, so these would have been equivalent anyway (except for
|
||||||
// UID).
|
// UID).
|
||||||
removeAllPendingRequests(newRequest.getTetheringType());
|
removeAllPendingRequests(newRequest.getTetheringType());
|
||||||
mPendingRequests.add(new PendingRequest(newRequest));
|
mPendingRequests.add(newRequest);
|
||||||
return AddResult.SUCCESS;
|
return AddResult.SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,10 +166,8 @@ public class RequestTracker {
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public TetheringRequest getNextPendingRequest(int type) {
|
public TetheringRequest getNextPendingRequest(int type) {
|
||||||
for (PendingRequest pendingRequest : mPendingRequests) {
|
for (TetheringRequest request : mPendingRequests) {
|
||||||
TetheringRequest tetheringRequest =
|
if (request.getTetheringType() == type) return request;
|
||||||
pendingRequest.getTetheringRequest();
|
|
||||||
if (tetheringRequest.getTetheringType() == type) return tetheringRequest;
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -158,7 +177,66 @@ public class RequestTracker {
|
||||||
*
|
*
|
||||||
* @param type Tethering type
|
* @param type Tethering type
|
||||||
*/
|
*/
|
||||||
public void removeAllPendingRequests(int type) {
|
public void removeAllPendingRequests(final int type) {
|
||||||
mPendingRequests.removeIf(r -> r.getTetheringRequest().getTetheringType() == type);
|
mPendingRequests.removeIf(r -> r.getTetheringType() == type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a specific pending request.
|
||||||
|
*
|
||||||
|
* Note: For V-, this will be the same as removeAllPendingRequests to align with historical
|
||||||
|
* behavior.
|
||||||
|
*
|
||||||
|
* @param request Request to be removed
|
||||||
|
*/
|
||||||
|
public void removePendingRequest(@NonNull TetheringRequest request) {
|
||||||
|
if (!mUseFuzzyMatching) {
|
||||||
|
// Remove all requests of the same type to match the historical behavior.
|
||||||
|
removeAllPendingRequests(request.getTetheringType());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mPendingRequests.removeIf(r -> r.equals(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a tethering request from the pending list and promotes it to serving with the
|
||||||
|
* IpServer that is using it.
|
||||||
|
* Note: If mUseFuzzyMatching is false, then the request will be removed from the pending list,
|
||||||
|
* but it will not be added to serving list.
|
||||||
|
*/
|
||||||
|
public void promoteRequestToServing(@NonNull final IpServer ipServer,
|
||||||
|
@NonNull final TetheringRequest tetheringRequest) {
|
||||||
|
removePendingRequest(tetheringRequest);
|
||||||
|
if (!mUseFuzzyMatching) return;
|
||||||
|
mServingRequests.put(ipServer, tetheringRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the serving request tied to the given IpServer, or null if there is none.
|
||||||
|
* Note: If mUseFuzzyMatching is false, then this will always return null.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public TetheringRequest getServingRequest(@NonNull final IpServer ipServer) {
|
||||||
|
return mServingRequests.get(ipServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the serving request tied to the given IpServer.
|
||||||
|
* Note: If mUseFuzzyMatching is false, then this is a no-op since serving requests are unused
|
||||||
|
* for that configuration.
|
||||||
|
*/
|
||||||
|
public void removeServingRequest(@NonNull final IpServer ipServer) {
|
||||||
|
mServingRequests.remove(ipServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all serving requests of the given tethering type.
|
||||||
|
*
|
||||||
|
* @param type Tethering type
|
||||||
|
*/
|
||||||
|
public void removeAllServingRequests(final int type) {
|
||||||
|
mServingRequests.entrySet().removeIf(e -> e.getValue().getTetheringType() == type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ import static android.net.TetheringManager.TETHERING_WIFI;
|
||||||
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
|
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
|
||||||
import static android.net.TetheringManager.TETHERING_WIGIG;
|
import static android.net.TetheringManager.TETHERING_WIGIG;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_BLUETOOTH_SERVICE_PENDING;
|
import static android.net.TetheringManager.TETHER_ERROR_BLUETOOTH_SERVICE_PENDING;
|
||||||
|
import static android.net.TetheringManager.TETHER_ERROR_DUPLICATE_REQUEST;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
|
import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
|
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
|
import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
|
||||||
|
|
@ -67,6 +68,8 @@ import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
|
||||||
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
|
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
|
||||||
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||||
|
|
||||||
|
import static com.android.networkstack.tethering.RequestTracker.AddResult.FAILURE_DUPLICATE_REQUEST_ERROR;
|
||||||
|
import static com.android.networkstack.tethering.RequestTracker.AddResult.FAILURE_DUPLICATE_REQUEST_RESTART;
|
||||||
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_FORCE_USB_FUNCTIONS;
|
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_FORCE_USB_FUNCTIONS;
|
||||||
import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
|
import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
|
||||||
import static com.android.networkstack.tethering.UpstreamNetworkMonitor.isCellular;
|
import static com.android.networkstack.tethering.UpstreamNetworkMonitor.isCellular;
|
||||||
|
|
@ -309,7 +312,7 @@ public class Tethering {
|
||||||
mLooper = mDeps.makeTetheringLooper();
|
mLooper = mDeps.makeTetheringLooper();
|
||||||
mNotificationUpdater = mDeps.makeNotificationUpdater(mContext, mLooper);
|
mNotificationUpdater = mDeps.makeNotificationUpdater(mContext, mLooper);
|
||||||
mTetheringMetrics = mDeps.makeTetheringMetrics(mContext);
|
mTetheringMetrics = mDeps.makeTetheringMetrics(mContext);
|
||||||
mRequestTracker = new RequestTracker();
|
mRequestTracker = new RequestTracker(isTetheringWithSoftApConfigEnabled());
|
||||||
|
|
||||||
mTetherStates = new ArrayMap<>();
|
mTetherStates = new ArrayMap<>();
|
||||||
mConnectedClientsTracker = new ConnectedClientsTracker();
|
mConnectedClientsTracker = new ConnectedClientsTracker();
|
||||||
|
|
@ -460,6 +463,7 @@ public class Tethering {
|
||||||
return mSettingsObserver;
|
return mSettingsObserver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Replace with SdkLevel.isAtLeastB() once the feature is fully implemented.
|
||||||
boolean isTetheringWithSoftApConfigEnabled() {
|
boolean isTetheringWithSoftApConfigEnabled() {
|
||||||
return SdkLevel.isAtLeastB() && Flags.tetheringWithSoftApConfig();
|
return SdkLevel.isAtLeastB() && Flags.tetheringWithSoftApConfig();
|
||||||
}
|
}
|
||||||
|
|
@ -704,9 +708,16 @@ public class Tethering {
|
||||||
RequestTracker.AddResult result = mRequestTracker.addPendingRequest(request);
|
RequestTracker.AddResult result = mRequestTracker.addPendingRequest(request);
|
||||||
// If tethering is already pending with a conflicting request, stop tethering before
|
// If tethering is already pending with a conflicting request, stop tethering before
|
||||||
// starting.
|
// starting.
|
||||||
if (result == RequestTracker.AddResult.FAILURE_CONFLICTING_PENDING_REQUEST) {
|
if (result == FAILURE_DUPLICATE_REQUEST_RESTART) {
|
||||||
stopTetheringInternal(type); // Also removes the request from the tracker.
|
stopTetheringInternal(type); // Also removes the request from the tracker.
|
||||||
mRequestTracker.addPendingRequest(request);
|
mRequestTracker.addPendingRequest(request);
|
||||||
|
} else if (result == FAILURE_DUPLICATE_REQUEST_ERROR) {
|
||||||
|
// Reject any fuzzy matched request.
|
||||||
|
// TODO: Add a CTS test to verify back-to-back start/stop calls succeed. This must
|
||||||
|
// be for a non-Wifi type, since Wifi will reject the start calls if it hasn't
|
||||||
|
// brought down the SoftAP yet.
|
||||||
|
sendTetherResult(listener, TETHER_ERROR_DUPLICATE_REQUEST);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.isExemptFromEntitlementCheck()) {
|
if (request.isExemptFromEntitlementCheck()) {
|
||||||
|
|
@ -731,7 +742,8 @@ public class Tethering {
|
||||||
for (TetherState state : mTetherStates.values()) {
|
for (TetherState state : mTetherStates.values()) {
|
||||||
// TODO: isCurrentlyServing only starts returning true once the IpServer has processed
|
// TODO: isCurrentlyServing only starts returning true once the IpServer has processed
|
||||||
// the CMD_TETHER_REQUESTED. Ensure that we consider the request to be serving even when
|
// the CMD_TETHER_REQUESTED. Ensure that we consider the request to be serving even when
|
||||||
// that has not happened yet.
|
// that has not happened yet. This already works if the sync state machine is enabled
|
||||||
|
// (which includes all B+ devices).
|
||||||
if (state.isCurrentlyServing() && state.ipServer.interfaceType() == type) return true;
|
if (state.isCurrentlyServing() && state.ipServer.interfaceType() == type) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -742,23 +754,24 @@ public class Tethering {
|
||||||
if (!isTetheringWithSoftApConfigEnabled()) return;
|
if (!isTetheringWithSoftApConfigEnabled()) return;
|
||||||
mHandler.post(() -> {
|
mHandler.post(() -> {
|
||||||
final int type = request.getTetheringType();
|
final int type = request.getTetheringType();
|
||||||
|
// TODO: when fuzzy-matching is enabled, check whether any pending request
|
||||||
|
// fuzzy-matches this request instead of just checking the type.
|
||||||
if (isTetheringTypePendingOrServing(type)) {
|
if (isTetheringTypePendingOrServing(type)) {
|
||||||
stopTetheringInternal(type);
|
stopTetheringInternal(type);
|
||||||
try {
|
// TODO: We should send the success result after the waiting for tethering to
|
||||||
listener.onResult(TETHER_ERROR_NO_ERROR);
|
// actually stop.
|
||||||
} catch (RemoteException ignored) { }
|
sendTetherResult(listener, TETHER_ERROR_NO_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request doesn't match any active requests, ignore.
|
// Request doesn't match any active requests, ignore.
|
||||||
try {
|
sendTetherResult(listener, TETHER_ERROR_UNKNOWN_REQUEST);
|
||||||
listener.onResult(TETHER_ERROR_UNKNOWN_REQUEST);
|
|
||||||
} catch (RemoteException ignored) { }
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopTetheringInternal(int type) {
|
void stopTetheringInternal(int type) {
|
||||||
mRequestTracker.removeAllPendingRequests(type);
|
mRequestTracker.removeAllPendingRequests(type);
|
||||||
|
mRequestTracker.removeAllServingRequests(type);
|
||||||
|
|
||||||
// Using a placeholder here is ok since none of the disable APIs use the request for
|
// Using a placeholder here is ok since none of the disable APIs use the request for
|
||||||
// anything. We simply need the tethering type to know which link layer to poke for removal.
|
// anything. We simply need the tethering type to know which link layer to poke for removal.
|
||||||
|
|
@ -802,21 +815,25 @@ public class Tethering {
|
||||||
// The result of Bluetooth tethering will be sent after the pan service connects.
|
// The result of Bluetooth tethering will be sent after the pan service connects.
|
||||||
if (result == TETHER_ERROR_BLUETOOTH_SERVICE_PENDING) return;
|
if (result == TETHER_ERROR_BLUETOOTH_SERVICE_PENDING) return;
|
||||||
|
|
||||||
sendTetherResult(listener, result, type);
|
sendTetherResultAndRemoveOnError(request, listener, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendTetherResult(final IIntResultListener listener, final int result,
|
private void sendTetherResult(final IIntResultListener listener, final int result) {
|
||||||
final int type) {
|
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
try {
|
try {
|
||||||
listener.onResult(result);
|
listener.onResult(result);
|
||||||
} catch (RemoteException e) { }
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendTetherResultAndRemoveOnError(TetheringRequest request,
|
||||||
|
final IIntResultListener listener, final int result) {
|
||||||
|
sendTetherResult(listener, result);
|
||||||
|
|
||||||
// If changing tethering fail, remove corresponding request
|
|
||||||
// no matter who trigger the start/stop.
|
|
||||||
if (result != TETHER_ERROR_NO_ERROR) {
|
if (result != TETHER_ERROR_NO_ERROR) {
|
||||||
mRequestTracker.removeAllPendingRequests(type);
|
mRequestTracker.removePendingRequest(request);
|
||||||
|
final int type = request.getTetheringType();
|
||||||
mTetheringMetrics.updateErrorCode(type, result);
|
mTetheringMetrics.updateErrorCode(type, result);
|
||||||
mTetheringMetrics.sendReport(type);
|
mTetheringMetrics.sendReport(type);
|
||||||
}
|
}
|
||||||
|
|
@ -862,8 +879,7 @@ public class Tethering {
|
||||||
// the service just to stop tethering since tethering is not started. Just remove
|
// the service just to stop tethering since tethering is not started. Just remove
|
||||||
// any pending request to enable tethering, and notify them that they have failed.
|
// any pending request to enable tethering, and notify them that they have failed.
|
||||||
if (mPendingPanRequestListener != null) {
|
if (mPendingPanRequestListener != null) {
|
||||||
sendTetherResult(mPendingPanRequestListener, TETHER_ERROR_SERVICE_UNAVAIL,
|
sendTetherResult(mPendingPanRequestListener, TETHER_ERROR_SERVICE_UNAVAIL);
|
||||||
TETHERING_BLUETOOTH);
|
|
||||||
}
|
}
|
||||||
mPendingPanRequestListener = null;
|
mPendingPanRequestListener = null;
|
||||||
return TETHER_ERROR_NO_ERROR;
|
return TETHER_ERROR_NO_ERROR;
|
||||||
|
|
@ -904,7 +920,10 @@ public class Tethering {
|
||||||
if (mPendingPanRequestListener != null) {
|
if (mPendingPanRequestListener != null) {
|
||||||
final int result = setBluetoothTetheringSettings(mBluetoothPan,
|
final int result = setBluetoothTetheringSettings(mBluetoothPan,
|
||||||
true /* enable */);
|
true /* enable */);
|
||||||
sendTetherResult(mPendingPanRequestListener, result, TETHERING_BLUETOOTH);
|
sendTetherResultAndRemoveOnError(
|
||||||
|
mRequestTracker.getOrCreatePendingRequest(TETHERING_BLUETOOTH),
|
||||||
|
mPendingPanRequestListener,
|
||||||
|
result);
|
||||||
}
|
}
|
||||||
mPendingPanRequestListener = null;
|
mPendingPanRequestListener = null;
|
||||||
});
|
});
|
||||||
|
|
@ -918,8 +937,10 @@ public class Tethering {
|
||||||
mIsConnected = false;
|
mIsConnected = false;
|
||||||
|
|
||||||
if (mPendingPanRequestListener != null) {
|
if (mPendingPanRequestListener != null) {
|
||||||
sendTetherResult(mPendingPanRequestListener, TETHER_ERROR_SERVICE_UNAVAIL,
|
sendTetherResultAndRemoveOnError(
|
||||||
TETHERING_BLUETOOTH);
|
mRequestTracker.getOrCreatePendingRequest(TETHERING_BLUETOOTH),
|
||||||
|
mPendingPanRequestListener,
|
||||||
|
TETHER_ERROR_SERVICE_UNAVAIL);
|
||||||
}
|
}
|
||||||
mPendingPanRequestListener = null;
|
mPendingPanRequestListener = null;
|
||||||
mBluetoothIfaceRequest = null;
|
mBluetoothIfaceRequest = null;
|
||||||
|
|
@ -1093,9 +1114,7 @@ public class Tethering {
|
||||||
final int type = ifaceNameToType(iface);
|
final int type = ifaceNameToType(iface);
|
||||||
if (type == TETHERING_INVALID) {
|
if (type == TETHERING_INVALID) {
|
||||||
Log.e(TAG, "Ignoring call to legacy tether for unknown iface " + iface);
|
Log.e(TAG, "Ignoring call to legacy tether for unknown iface " + iface);
|
||||||
try {
|
sendTetherResult(listener, TETHER_ERROR_UNKNOWN_IFACE);
|
||||||
listener.onResult(TETHER_ERROR_UNKNOWN_IFACE);
|
|
||||||
} catch (RemoteException e) { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TetheringRequest request = mRequestTracker.getNextPendingRequest(type);
|
TetheringRequest request = mRequestTracker.getNextPendingRequest(type);
|
||||||
|
|
@ -1132,9 +1151,7 @@ public class Tethering {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
try {
|
sendTetherResult(listener, result);
|
||||||
listener.onResult(result);
|
|
||||||
} catch (RemoteException e) { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1167,11 +1184,9 @@ public class Tethering {
|
||||||
Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");
|
Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");
|
||||||
return TETHER_ERROR_UNAVAIL_IFACE;
|
return TETHER_ERROR_UNAVAIL_IFACE;
|
||||||
}
|
}
|
||||||
|
mRequestTracker.promoteRequestToServing(tetherState.ipServer, request);
|
||||||
// NOTE: If a CMD_TETHER_REQUESTED message is already in the IpServer's queue but not yet
|
// NOTE: If a CMD_TETHER_REQUESTED message is already in the IpServer's queue but not yet
|
||||||
// processed, this will be a no-op and it will not return an error.
|
// processed, this will be a no-op and it will not return an error.
|
||||||
//
|
|
||||||
// This code cannot race with untether() because they both run on the handler thread.
|
|
||||||
mRequestTracker.removeAllPendingRequests(request.getTetheringType());
|
|
||||||
tetherState.ipServer.enable(request);
|
tetherState.ipServer.enable(request);
|
||||||
if (request.getRequestType() == REQUEST_TYPE_PLACEHOLDER) {
|
if (request.getRequestType() == REQUEST_TYPE_PLACEHOLDER) {
|
||||||
TerribleErrorLog.logTerribleError(TetheringStatsLog::write,
|
TerribleErrorLog.logTerribleError(TetheringStatsLog::write,
|
||||||
|
|
@ -1191,10 +1206,7 @@ public class Tethering {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mHandler.post(() -> {
|
mHandler.post(() -> {
|
||||||
try {
|
sendTetherResult(listener, legacyUntetherInternal(iface));
|
||||||
listener.onResult(legacyUntetherInternal(iface));
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1209,7 +1221,7 @@ public class Tethering {
|
||||||
Log.e(TAG, "Tried to untether an inactive iface :" + iface + ", ignoring");
|
Log.e(TAG, "Tried to untether an inactive iface :" + iface + ", ignoring");
|
||||||
return TETHER_ERROR_UNAVAIL_IFACE;
|
return TETHER_ERROR_UNAVAIL_IFACE;
|
||||||
}
|
}
|
||||||
tetherState.ipServer.unwanted();
|
ensureIpServerUnwanted(tetherState.ipServer);
|
||||||
return TETHER_ERROR_NO_ERROR;
|
return TETHER_ERROR_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1289,7 +1301,12 @@ public class Tethering {
|
||||||
final TetherState tetherState = mTetherStates.valueAt(i);
|
final TetherState tetherState = mTetherStates.valueAt(i);
|
||||||
final int type = tetherState.ipServer.interfaceType();
|
final int type = tetherState.ipServer.interfaceType();
|
||||||
final String iface = mTetherStates.keyAt(i);
|
final String iface = mTetherStates.keyAt(i);
|
||||||
final TetheringRequest request = tetherState.ipServer.getTetheringRequest();
|
// Note: serving requests are only populated on B+. B+ also uses the sync state
|
||||||
|
// machine by default. This ensures that the serving request is (correctly) populated
|
||||||
|
// after the IpServer enters the available state and before it enters the serving
|
||||||
|
// state.
|
||||||
|
final TetheringRequest request =
|
||||||
|
mRequestTracker.getServingRequest(tetherState.ipServer);
|
||||||
final boolean includeSoftApConfig = request != null && cookie != null
|
final boolean includeSoftApConfig = request != null && cookie != null
|
||||||
&& (cookie.uid == request.getUid() || cookie.hasSystemPrivilege);
|
&& (cookie.uid == request.getUid() || cookie.hasSystemPrivilege);
|
||||||
final TetheringInterface tetheringIface = new TetheringInterface(type, iface,
|
final TetheringInterface tetheringIface = new TetheringInterface(type, iface,
|
||||||
|
|
@ -1611,7 +1628,7 @@ public class Tethering {
|
||||||
|
|
||||||
private void disableWifiIpServingCommon(int tetheringType, String ifname) {
|
private void disableWifiIpServingCommon(int tetheringType, String ifname) {
|
||||||
if (!TextUtils.isEmpty(ifname) && mTetherStates.containsKey(ifname)) {
|
if (!TextUtils.isEmpty(ifname) && mTetherStates.containsKey(ifname)) {
|
||||||
mTetherStates.get(ifname).ipServer.unwanted();
|
ensureIpServerUnwanted(mTetherStates.get(ifname).ipServer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1628,7 +1645,7 @@ public class Tethering {
|
||||||
for (int i = 0; i < mTetherStates.size(); i++) {
|
for (int i = 0; i < mTetherStates.size(); i++) {
|
||||||
final IpServer ipServer = mTetherStates.valueAt(i).ipServer;
|
final IpServer ipServer = mTetherStates.valueAt(i).ipServer;
|
||||||
if (ipServer.interfaceType() == tetheringType) {
|
if (ipServer.interfaceType() == tetheringType) {
|
||||||
ipServer.unwanted();
|
ensureIpServerUnwanted(ipServer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1775,9 +1792,7 @@ public class Tethering {
|
||||||
|
|
||||||
void setUsbTethering(boolean enable, IIntResultListener listener) {
|
void setUsbTethering(boolean enable, IIntResultListener listener) {
|
||||||
mHandler.post(() -> {
|
mHandler.post(() -> {
|
||||||
try {
|
sendTetherResult(listener, setUsbTethering(enable));
|
||||||
listener.onResult(setUsbTethering(enable));
|
|
||||||
} catch (RemoteException e) { }
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2904,6 +2919,9 @@ public class Tethering {
|
||||||
tetherState.lastState = state;
|
tetherState.lastState = state;
|
||||||
tetherState.lastError = lastError;
|
tetherState.lastError = lastError;
|
||||||
} else {
|
} else {
|
||||||
|
// Note: Even if an IpServer exists for this iface, it may be different from "who"
|
||||||
|
// if a new IpServer fills the gap before the IpServer.STATE_UNAVAILABLE transition.
|
||||||
|
// TODO: remove this comment once the sync state machine is enabled everywhere.
|
||||||
if (DBG) Log.d(TAG, "got notification from stale iface " + iface);
|
if (DBG) Log.d(TAG, "got notification from stale iface " + iface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2918,7 +2936,19 @@ public class Tethering {
|
||||||
int which;
|
int which;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case IpServer.STATE_UNAVAILABLE:
|
case IpServer.STATE_UNAVAILABLE:
|
||||||
|
which = TetherMainSM.EVENT_IFACE_SERVING_STATE_INACTIVE;
|
||||||
|
break;
|
||||||
case IpServer.STATE_AVAILABLE:
|
case IpServer.STATE_AVAILABLE:
|
||||||
|
if (lastError != TETHER_ERROR_NO_ERROR) {
|
||||||
|
// IpServer transitioned from an enabled state (STATE_TETHERED or
|
||||||
|
// STATE_LOCAL_ONLY) back to STATE_AVAILABLE due to an error, so make sure
|
||||||
|
// we remove the serving request from RequestTracker.
|
||||||
|
// TODO: don't continue to use IpServers after they have hit an error, and
|
||||||
|
// instead move them to STATE_UNAVAILABLE. This code can then
|
||||||
|
// unconditionally remove the serving request whenever the IpServer enters
|
||||||
|
// STATE_UNAVAILABLE.
|
||||||
|
mRequestTracker.removeServingRequest(who);
|
||||||
|
}
|
||||||
which = TetherMainSM.EVENT_IFACE_SERVING_STATE_INACTIVE;
|
which = TetherMainSM.EVENT_IFACE_SERVING_STATE_INACTIVE;
|
||||||
break;
|
break;
|
||||||
case IpServer.STATE_TETHERED:
|
case IpServer.STATE_TETHERED:
|
||||||
|
|
@ -3014,11 +3044,18 @@ public class Tethering {
|
||||||
final TetherState tetherState = mTetherStates.get(iface);
|
final TetherState tetherState = mTetherStates.get(iface);
|
||||||
if (tetherState == null) return;
|
if (tetherState == null) return;
|
||||||
|
|
||||||
|
mRequestTracker.removeServingRequest(tetherState.ipServer);
|
||||||
tetherState.ipServer.stop();
|
tetherState.ipServer.stop();
|
||||||
mLog.i("removing IpServer for: " + iface);
|
mLog.i("removing IpServer for: " + iface);
|
||||||
mTetherStates.remove(iface);
|
mTetherStates.remove(iface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ensureIpServerUnwanted(final IpServer ipServer) {
|
||||||
|
mLog.i("unrequesting IpServer: " + ipServer);
|
||||||
|
mRequestTracker.removeServingRequest(ipServer);
|
||||||
|
ipServer.unwanted();
|
||||||
|
}
|
||||||
|
|
||||||
private static String[] copy(String[] strarray) {
|
private static String[] copy(String[] strarray) {
|
||||||
return Arrays.copyOf(strarray, strarray.length);
|
return Arrays.copyOf(strarray, strarray.length);
|
||||||
}
|
}
|
||||||
|
|
@ -3026,9 +3063,7 @@ public class Tethering {
|
||||||
void setPreferTestNetworks(final boolean prefer, IIntResultListener listener) {
|
void setPreferTestNetworks(final boolean prefer, IIntResultListener listener) {
|
||||||
mHandler.post(() -> {
|
mHandler.post(() -> {
|
||||||
mUpstreamNetworkMonitor.setPreferTestNetworks(prefer);
|
mUpstreamNetworkMonitor.setPreferTestNetworks(prefer);
|
||||||
try {
|
sendTetherResult(listener, TETHER_ERROR_NO_ERROR);
|
||||||
listener.onResult(TETHER_ERROR_NO_ERROR);
|
|
||||||
} catch (RemoteException e) { }
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,19 +18,22 @@ package com.android.networkstack.tethering;
|
||||||
|
|
||||||
import static android.net.TetheringManager.TETHERING_USB;
|
import static android.net.TetheringManager.TETHERING_USB;
|
||||||
import static android.net.TetheringManager.TETHERING_WIFI;
|
import static android.net.TetheringManager.TETHERING_WIFI;
|
||||||
|
import static android.net.TetheringManager.TETHERING_VIRTUAL;
|
||||||
|
|
||||||
import static com.android.networkstack.tethering.util.TetheringUtils.createPlaceholderRequest;
|
import static com.android.networkstack.tethering.util.TetheringUtils.createPlaceholderRequest;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
import android.net.TetheringManager.TetheringRequest;
|
import android.net.TetheringManager.TetheringRequest;
|
||||||
|
import android.net.ip.IpServer;
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
import com.android.networkstack.tethering.RequestTracker.AddResult;
|
import com.android.networkstack.tethering.RequestTracker.AddResult;
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
|
@ -39,13 +42,10 @@ import org.junit.runner.RunWith;
|
||||||
public class RequestTrackerTest {
|
public class RequestTrackerTest {
|
||||||
private RequestTracker mRequestTracker;
|
private RequestTracker mRequestTracker;
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
mRequestTracker = new RequestTracker();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoRequestsAdded_noPendingRequests() {
|
public void testNoRequestsAdded_noPendingRequests() {
|
||||||
|
mRequestTracker = new RequestTracker(false);
|
||||||
|
|
||||||
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_WIFI)).isNull();
|
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_WIFI)).isNull();
|
||||||
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_WIFI))
|
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_WIFI))
|
||||||
.isEqualTo(createPlaceholderRequest(TETHERING_WIFI));
|
.isEqualTo(createPlaceholderRequest(TETHERING_WIFI));
|
||||||
|
|
@ -53,6 +53,7 @@ public class RequestTrackerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddRequest_successResultAndBecomesNextPending() {
|
public void testAddRequest_successResultAndBecomesNextPending() {
|
||||||
|
mRequestTracker = new RequestTracker(false);
|
||||||
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||||
|
|
||||||
final AddResult result = mRequestTracker.addPendingRequest(request);
|
final AddResult result = mRequestTracker.addPendingRequest(request);
|
||||||
|
|
@ -64,6 +65,7 @@ public class RequestTrackerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddRequest_equalRequestExists_successResultAndBecomesNextPending() {
|
public void testAddRequest_equalRequestExists_successResultAndBecomesNextPending() {
|
||||||
|
mRequestTracker = new RequestTracker(false);
|
||||||
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||||
mRequestTracker.addPendingRequest(request);
|
mRequestTracker.addPendingRequest(request);
|
||||||
|
|
||||||
|
|
@ -77,6 +79,7 @@ public class RequestTrackerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddRequest_equalButDifferentUidRequest_successResultAndBecomesNextPending() {
|
public void testAddRequest_equalButDifferentUidRequest_successResultAndBecomesNextPending() {
|
||||||
|
mRequestTracker = new RequestTracker(false);
|
||||||
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||||
request.setUid(1000);
|
request.setUid(1000);
|
||||||
request.setPackageName("package");
|
request.setPackageName("package");
|
||||||
|
|
@ -94,7 +97,8 @@ public class RequestTrackerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddConflictingRequest_returnsFailureConflictingPendingRequest() {
|
public void testAddRequest_conflictingPendingRequest_returnsFailureConflictingRequestRestart() {
|
||||||
|
mRequestTracker = new RequestTracker(false);
|
||||||
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||||
final TetheringRequest conflictingRequest = new TetheringRequest.Builder(TETHERING_WIFI)
|
final TetheringRequest conflictingRequest = new TetheringRequest.Builder(TETHERING_WIFI)
|
||||||
.setExemptFromEntitlementCheck(true).build();
|
.setExemptFromEntitlementCheck(true).build();
|
||||||
|
|
@ -102,13 +106,139 @@ public class RequestTrackerTest {
|
||||||
|
|
||||||
final AddResult result = mRequestTracker.addPendingRequest(conflictingRequest);
|
final AddResult result = mRequestTracker.addPendingRequest(conflictingRequest);
|
||||||
|
|
||||||
assertThat(result).isEqualTo(AddResult.FAILURE_CONFLICTING_PENDING_REQUEST);
|
assertThat(result).isEqualTo(AddResult.FAILURE_DUPLICATE_REQUEST_RESTART);
|
||||||
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_WIFI)).isEqualTo(request);
|
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_WIFI)).isEqualTo(request);
|
||||||
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_WIFI)).isEqualTo(request);
|
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_WIFI)).isEqualTo(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddRequest_noExistingRequestsFuzzyMatching_returnsSuccess() {
|
||||||
|
mRequestTracker = new RequestTracker(true);
|
||||||
|
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||||
|
|
||||||
|
final AddResult result = mRequestTracker.addPendingRequest(request);
|
||||||
|
|
||||||
|
assertThat(result).isEqualTo(AddResult.SUCCESS);
|
||||||
|
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_WIFI)).isEqualTo(request);
|
||||||
|
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_WIFI)).isEqualTo(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddRequest_conflictingPendingRequestFuzzyMatching_returnsFailure() {
|
||||||
|
mRequestTracker = new RequestTracker(true);
|
||||||
|
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||||
|
final TetheringRequest conflictingRequest = new TetheringRequest.Builder(TETHERING_WIFI)
|
||||||
|
.setExemptFromEntitlementCheck(true).build();
|
||||||
|
mRequestTracker.addPendingRequest(request);
|
||||||
|
|
||||||
|
final AddResult result = mRequestTracker.addPendingRequest(conflictingRequest);
|
||||||
|
|
||||||
|
assertThat(result).isEqualTo(AddResult.FAILURE_DUPLICATE_REQUEST_ERROR);
|
||||||
|
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_WIFI)).isEqualTo(request);
|
||||||
|
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_WIFI)).isEqualTo(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddRequest_conflictingServingRequestFuzzyMatching_returnsFailure() {
|
||||||
|
mRequestTracker = new RequestTracker(true);
|
||||||
|
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||||
|
final TetheringRequest conflictingRequest = new TetheringRequest.Builder(TETHERING_WIFI)
|
||||||
|
.setExemptFromEntitlementCheck(true).build();
|
||||||
|
mRequestTracker.promoteRequestToServing(mock(IpServer.class), request);
|
||||||
|
|
||||||
|
final AddResult result = mRequestTracker.addPendingRequest(conflictingRequest);
|
||||||
|
|
||||||
|
assertThat(result).isEqualTo(AddResult.FAILURE_DUPLICATE_REQUEST_ERROR);
|
||||||
|
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_WIFI)).isNull();
|
||||||
|
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_WIFI))
|
||||||
|
.isEqualTo(createPlaceholderRequest(TETHERING_WIFI));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddRequest_nonMatchingPendingRequestFuzzyMatching_returnsSuccess() {
|
||||||
|
mRequestTracker = new RequestTracker(true);
|
||||||
|
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_VIRTUAL).build();
|
||||||
|
final TetheringRequest nonFuzzyMatched = new TetheringRequest.Builder(TETHERING_VIRTUAL)
|
||||||
|
.setInterfaceName("iface")
|
||||||
|
.build();
|
||||||
|
mRequestTracker.addPendingRequest(request);
|
||||||
|
|
||||||
|
final AddResult result = mRequestTracker.addPendingRequest(nonFuzzyMatched);
|
||||||
|
|
||||||
|
assertThat(result).isEqualTo(AddResult.SUCCESS);
|
||||||
|
// Next request is still the first, but verify RequestTracker contains the second request by
|
||||||
|
// seeing if it rejects anything matching the second request
|
||||||
|
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_VIRTUAL)).isEqualTo(request);
|
||||||
|
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_VIRTUAL))
|
||||||
|
.isEqualTo(request);
|
||||||
|
assertThat(mRequestTracker.addPendingRequestFuzzyMatched(nonFuzzyMatched))
|
||||||
|
.isEqualTo(AddResult.FAILURE_DUPLICATE_REQUEST_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddRequest_nonMatchingServingRequestFuzzyMatching_returnsSuccess() {
|
||||||
|
mRequestTracker = new RequestTracker(true);
|
||||||
|
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_VIRTUAL).build();
|
||||||
|
final TetheringRequest nonFuzzyMatched = new TetheringRequest.Builder(TETHERING_VIRTUAL)
|
||||||
|
.setInterfaceName("iface")
|
||||||
|
.build();
|
||||||
|
mRequestTracker.promoteRequestToServing(mock(IpServer.class), request);
|
||||||
|
|
||||||
|
final AddResult result = mRequestTracker.addPendingRequest(nonFuzzyMatched);
|
||||||
|
|
||||||
|
assertThat(result).isEqualTo(AddResult.SUCCESS);
|
||||||
|
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_VIRTUAL))
|
||||||
|
.isEqualTo(nonFuzzyMatched);
|
||||||
|
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_VIRTUAL))
|
||||||
|
.isEqualTo(nonFuzzyMatched);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemovePendingRequest_removesAllPendingRequestsOfType() {
|
||||||
|
mRequestTracker = new RequestTracker(false);
|
||||||
|
final TetheringRequest request1 = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||||
|
request1.setUid(1000);
|
||||||
|
request1.setPackageName("package");
|
||||||
|
mRequestTracker.addPendingRequest(request1);
|
||||||
|
final TetheringRequest request2 = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||||
|
request2.setUid(2000);
|
||||||
|
request2.setPackageName("package2");
|
||||||
|
|
||||||
|
mRequestTracker.removePendingRequest(request2);
|
||||||
|
|
||||||
|
// Verify request1 isn't pending even though we tried to remove a different request
|
||||||
|
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_WIFI)).isNull();
|
||||||
|
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_WIFI))
|
||||||
|
.isEqualTo(createPlaceholderRequest(TETHERING_WIFI));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemovePendingRequest_fuzzyMatching_onlyTheEqualRequestIsRemoved() {
|
||||||
|
mRequestTracker = new RequestTracker(true);
|
||||||
|
final TetheringRequest request1 = new TetheringRequest.Builder(TETHERING_VIRTUAL).build();
|
||||||
|
final TetheringRequest request2 = new TetheringRequest.Builder(TETHERING_VIRTUAL)
|
||||||
|
.setInterfaceName("iface")
|
||||||
|
.build();
|
||||||
|
mRequestTracker.addPendingRequest(request1);
|
||||||
|
mRequestTracker.addPendingRequest(request2);
|
||||||
|
|
||||||
|
mRequestTracker.removePendingRequest(request2);
|
||||||
|
|
||||||
|
// Verify request1 is still pending.
|
||||||
|
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_VIRTUAL)).isEqualTo(request1);
|
||||||
|
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_VIRTUAL))
|
||||||
|
.isEqualTo(request1);
|
||||||
|
assertThat(mRequestTracker.addPendingRequestFuzzyMatched(request1))
|
||||||
|
.isEqualTo(AddResult.FAILURE_DUPLICATE_REQUEST_ERROR);
|
||||||
|
// Verify we've removed request2 by checking if it can be added back without
|
||||||
|
// FAILURE_CONFLICTING_REQUEST_FAIL.
|
||||||
|
assertThat(mRequestTracker.addPendingRequestFuzzyMatched(request2))
|
||||||
|
.isEqualTo(AddResult.SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRemoveAllPendingRequests_noPendingRequestsLeft() {
|
public void testRemoveAllPendingRequests_noPendingRequestsLeft() {
|
||||||
|
mRequestTracker = new RequestTracker(false);
|
||||||
final TetheringRequest firstRequest = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
final TetheringRequest firstRequest = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||||
firstRequest.setUid(1000);
|
firstRequest.setUid(1000);
|
||||||
firstRequest.setPackageName("package");
|
firstRequest.setPackageName("package");
|
||||||
|
|
@ -127,6 +257,7 @@ public class RequestTrackerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRemoveAllPendingRequests_differentTypeExists_doesNotRemoveDifferentType() {
|
public void testRemoveAllPendingRequests_differentTypeExists_doesNotRemoveDifferentType() {
|
||||||
|
mRequestTracker = new RequestTracker(false);
|
||||||
final TetheringRequest differentType = new TetheringRequest.Builder(TETHERING_USB).build();
|
final TetheringRequest differentType = new TetheringRequest.Builder(TETHERING_USB).build();
|
||||||
mRequestTracker.addPendingRequest(differentType);
|
mRequestTracker.addPendingRequest(differentType);
|
||||||
|
|
||||||
|
|
@ -136,4 +267,61 @@ public class RequestTrackerTest {
|
||||||
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_USB))
|
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_USB))
|
||||||
.isEqualTo(differentType);
|
.isEqualTo(differentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPromoteRequestToServing_requestIsntPendingAnymore() {
|
||||||
|
mRequestTracker = new RequestTracker(false);
|
||||||
|
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||||
|
mRequestTracker.addPendingRequest(request);
|
||||||
|
|
||||||
|
mRequestTracker.promoteRequestToServing(mock(IpServer.class), request);
|
||||||
|
|
||||||
|
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_WIFI)).isNull();
|
||||||
|
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_WIFI))
|
||||||
|
.isEqualTo(createPlaceholderRequest(TETHERING_WIFI));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPromoteRequestToServing_fuzzyMatching_requestIsntPendingAnymore() {
|
||||||
|
mRequestTracker = new RequestTracker(true);
|
||||||
|
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||||
|
mRequestTracker.addPendingRequest(request);
|
||||||
|
|
||||||
|
mRequestTracker.promoteRequestToServing(mock(IpServer.class), request);
|
||||||
|
|
||||||
|
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_WIFI)).isNull();
|
||||||
|
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_WIFI))
|
||||||
|
.isEqualTo(createPlaceholderRequest(TETHERING_WIFI));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveServingRequest_fuzzyMatching_requestCanBeAddedAgain() {
|
||||||
|
mRequestTracker = new RequestTracker(true);
|
||||||
|
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||||
|
mRequestTracker.addPendingRequest(request);
|
||||||
|
IpServer ipServer = mock(IpServer.class);
|
||||||
|
mRequestTracker.promoteRequestToServing(ipServer, request);
|
||||||
|
|
||||||
|
mRequestTracker.removeServingRequest(ipServer);
|
||||||
|
|
||||||
|
AddResult result = mRequestTracker.addPendingRequest(request);
|
||||||
|
assertThat(result).isEqualTo(AddResult.SUCCESS);
|
||||||
|
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_WIFI)).isEqualTo(request);
|
||||||
|
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_WIFI)).isEqualTo(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveAllServingRequests_fuzzyMatching_requestCanBeAddedAgain() {
|
||||||
|
mRequestTracker = new RequestTracker(true);
|
||||||
|
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||||
|
mRequestTracker.addPendingRequest(request);
|
||||||
|
mRequestTracker.promoteRequestToServing(mock(IpServer.class), request);
|
||||||
|
|
||||||
|
mRequestTracker.removeAllServingRequests(TETHERING_WIFI);
|
||||||
|
|
||||||
|
AddResult result = mRequestTracker.addPendingRequest(request);
|
||||||
|
assertThat(result).isEqualTo(AddResult.SUCCESS);
|
||||||
|
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_WIFI)).isEqualTo(request);
|
||||||
|
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_WIFI)).isEqualTo(request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ import static android.net.TetheringManager.TETHERING_USB;
|
||||||
import static android.net.TetheringManager.TETHERING_VIRTUAL;
|
import static android.net.TetheringManager.TETHERING_VIRTUAL;
|
||||||
import static android.net.TetheringManager.TETHERING_WIFI;
|
import static android.net.TetheringManager.TETHERING_WIFI;
|
||||||
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
|
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
|
||||||
|
import static android.net.TetheringManager.TETHER_ERROR_DUPLICATE_REQUEST;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
|
import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
|
import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
|
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
|
||||||
|
|
@ -59,6 +60,7 @@ import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
|
||||||
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
|
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
|
||||||
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
|
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
|
||||||
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
|
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
|
||||||
|
import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR;
|
||||||
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
|
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
|
||||||
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
|
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
|
||||||
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
|
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
|
||||||
|
|
@ -93,6 +95,7 @@ import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.junit.Assume.assumeFalse;
|
||||||
import static org.junit.Assume.assumeTrue;
|
import static org.junit.Assume.assumeTrue;
|
||||||
import static org.mockito.ArgumentMatchers.argThat;
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
import static org.mockito.ArgumentMatchers.notNull;
|
import static org.mockito.ArgumentMatchers.notNull;
|
||||||
|
|
@ -197,6 +200,7 @@ import androidx.test.runner.AndroidJUnit4;
|
||||||
import com.android.internal.util.test.BroadcastInterceptingContext;
|
import com.android.internal.util.test.BroadcastInterceptingContext;
|
||||||
import com.android.internal.util.test.FakeSettingsProvider;
|
import com.android.internal.util.test.FakeSettingsProvider;
|
||||||
import com.android.modules.utils.build.SdkLevel;
|
import com.android.modules.utils.build.SdkLevel;
|
||||||
|
import com.android.net.flags.Flags;
|
||||||
import com.android.net.module.util.CollectionUtils;
|
import com.android.net.module.util.CollectionUtils;
|
||||||
import com.android.net.module.util.InterfaceParams;
|
import com.android.net.module.util.InterfaceParams;
|
||||||
import com.android.net.module.util.PrivateAddressCoordinator;
|
import com.android.net.module.util.PrivateAddressCoordinator;
|
||||||
|
|
@ -308,8 +312,7 @@ public class TetheringTest {
|
||||||
@Mock private TetheringMetrics mTetheringMetrics;
|
@Mock private TetheringMetrics mTetheringMetrics;
|
||||||
@Mock private PrivateAddressCoordinator.Dependencies mPrivateAddressCoordinatorDependencies;
|
@Mock private PrivateAddressCoordinator.Dependencies mPrivateAddressCoordinatorDependencies;
|
||||||
|
|
||||||
private final MockIpServerDependencies mIpServerDependencies =
|
private MockIpServerDependencies mIpServerDependencies;
|
||||||
spy(new MockIpServerDependencies());
|
|
||||||
private final MockTetheringDependencies mTetheringDependencies =
|
private final MockTetheringDependencies mTetheringDependencies =
|
||||||
new MockTetheringDependencies();
|
new MockTetheringDependencies();
|
||||||
|
|
||||||
|
|
@ -396,6 +399,9 @@ public class TetheringTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MockIpServerDependencies extends IpServer.Dependencies {
|
public class MockIpServerDependencies extends IpServer.Dependencies {
|
||||||
|
|
||||||
|
private int mOnDhcpServerCreatedResult = STATUS_SUCCESS;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DadProxy getDadProxy(
|
public DadProxy getDadProxy(
|
||||||
Handler handler, InterfaceParams ifParams) {
|
Handler handler, InterfaceParams ifParams) {
|
||||||
|
|
@ -437,7 +443,7 @@ public class TetheringTest {
|
||||||
DhcpServerCallbacks cb) {
|
DhcpServerCallbacks cb) {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer);
|
cb.onDhcpServerCreated(mOnDhcpServerCreatedResult, mDhcpServer);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
fail(e.getMessage());
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
@ -448,6 +454,10 @@ public class TetheringTest {
|
||||||
IpNeighborMonitor.NeighborEventConsumer c) {
|
IpNeighborMonitor.NeighborEventConsumer c) {
|
||||||
return mIpNeighborMonitor;
|
return mIpNeighborMonitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setOnDhcpServerCreatedResult(final int result) {
|
||||||
|
mOnDhcpServerCreatedResult = result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MockTetheringDependencies extends TetheringDependencies {
|
public class MockTetheringDependencies extends TetheringDependencies {
|
||||||
|
|
@ -708,6 +718,7 @@ public class TetheringTest {
|
||||||
|
|
||||||
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)).thenReturn(true);
|
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)).thenReturn(true);
|
||||||
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)).thenReturn(true);
|
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)).thenReturn(true);
|
||||||
|
mIpServerDependencies = spy(new MockIpServerDependencies());
|
||||||
}
|
}
|
||||||
|
|
||||||
// In order to interact with syncSM from the test, tethering must be created in test thread.
|
// In order to interact with syncSM from the test, tethering must be created in test thread.
|
||||||
|
|
@ -2425,7 +2436,7 @@ public class TetheringTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSoftApConfigInTetheringEventCallback() throws Exception {
|
public void testSoftApConfigInTetheringEventCallback() throws Exception {
|
||||||
assumeTrue(SdkLevel.isAtLeastV());
|
assumeTrue(isTetheringWithSoftApConfigEnabled());
|
||||||
when(mContext.checkCallingOrSelfPermission(NETWORK_SETTINGS))
|
when(mContext.checkCallingOrSelfPermission(NETWORK_SETTINGS))
|
||||||
.thenReturn(PERMISSION_DENIED);
|
.thenReturn(PERMISSION_DENIED);
|
||||||
when(mContext.checkCallingOrSelfPermission(NETWORK_STACK))
|
when(mContext.checkCallingOrSelfPermission(NETWORK_STACK))
|
||||||
|
|
@ -2482,14 +2493,12 @@ public class TetheringTest {
|
||||||
// Enable wifi tethering
|
// Enable wifi tethering
|
||||||
mBinderCallingUid = TEST_CALLER_UID;
|
mBinderCallingUid = TEST_CALLER_UID;
|
||||||
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, null);
|
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, null);
|
||||||
|
mLooper.dispatchAll();
|
||||||
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
|
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
|
||||||
mLooper.dispatchAll();
|
mLooper.dispatchAll();
|
||||||
if (SdkLevel.isAtLeastB()) {
|
// Netd "up" event should not trigger a state change callback in B+.
|
||||||
// Starting in B, ignore the interfaceStatusChanged
|
callback.assertNoStateChangeCallback();
|
||||||
callback.assertNoStateChangeCallback();
|
|
||||||
}
|
|
||||||
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
|
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
|
||||||
mLooper.dispatchAll();
|
|
||||||
// Verify we see Available -> Tethered states
|
// Verify we see Available -> Tethered states
|
||||||
assertArrayEquals(new TetheringInterface[] {wifiIfaceWithoutConfig},
|
assertArrayEquals(new TetheringInterface[] {wifiIfaceWithoutConfig},
|
||||||
callback.pollTetherStatesChanged().availableList);
|
callback.pollTetherStatesChanged().availableList);
|
||||||
|
|
@ -2507,20 +2516,15 @@ public class TetheringTest {
|
||||||
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STARTED);
|
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STARTED);
|
||||||
|
|
||||||
// Disable wifi tethering
|
// Disable wifi tethering
|
||||||
mLooper.dispatchAll();
|
|
||||||
mTethering.stopTethering(TETHERING_WIFI);
|
mTethering.stopTethering(TETHERING_WIFI);
|
||||||
sendWifiApStateChanged(WIFI_AP_STATE_DISABLED);
|
mLooper.dispatchAll();
|
||||||
if (isAtLeastT()) {
|
sendWifiApStateChanged(WIFI_AP_STATE_DISABLED, TEST_WLAN_IFNAME,
|
||||||
// After T, tethering doesn't support WIFI_AP_STATE_DISABLED with null interface name.
|
|
||||||
callback.assertNoStateChangeCallback();
|
|
||||||
sendWifiApStateChanged(WIFI_AP_STATE_DISABLED, TEST_WLAN_IFNAME,
|
|
||||||
IFACE_IP_MODE_TETHERED);
|
IFACE_IP_MODE_TETHERED);
|
||||||
}
|
assertArrayEquals(new TetheringInterface[] {wifiIfaceWithoutConfig},
|
||||||
assertArrayEquals(new TetheringInterface[] {wifiIfaceWithConfig},
|
|
||||||
callback.pollTetherStatesChanged().availableList);
|
callback.pollTetherStatesChanged().availableList);
|
||||||
assertArrayEquals(new TetheringInterface[] {wifiIfaceWithoutConfig},
|
assertArrayEquals(new TetheringInterface[] {wifiIfaceWithoutConfig},
|
||||||
differentCallback.pollTetherStatesChanged().availableList);
|
differentCallback.pollTetherStatesChanged().availableList);
|
||||||
assertArrayEquals(new TetheringInterface[] {wifiIfaceWithConfig},
|
assertArrayEquals(new TetheringInterface[] {wifiIfaceWithoutConfig},
|
||||||
settingsCallback.pollTetherStatesChanged().availableList);
|
settingsCallback.pollTetherStatesChanged().availableList);
|
||||||
mLooper.dispatchAll();
|
mLooper.dispatchAll();
|
||||||
callback.expectUpstreamChanged(NULL_NETWORK);
|
callback.expectUpstreamChanged(NULL_NETWORK);
|
||||||
|
|
@ -2528,6 +2532,253 @@ public class TetheringTest {
|
||||||
callback.assertNoCallback();
|
callback.assertNoCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isTetheringWithSoftApConfigEnabled() {
|
||||||
|
return SdkLevel.isAtLeastB() && Flags.tetheringWithSoftApConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFuzzyMatchedWifiCannotBeAdded() throws Exception {
|
||||||
|
assumeTrue(isTetheringWithSoftApConfigEnabled());
|
||||||
|
initTetheringOnTestThread();
|
||||||
|
TestTetheringEventCallback callback = new TestTetheringEventCallback();
|
||||||
|
SoftApConfiguration softApConfig = new SoftApConfiguration.Builder().setWifiSsid(
|
||||||
|
WifiSsid.fromBytes("SoftApConfig".getBytes(StandardCharsets.UTF_8))).build();
|
||||||
|
final TetheringInterface wifiIfaceWithoutConfig = new TetheringInterface(
|
||||||
|
TETHERING_WIFI, TEST_WLAN_IFNAME, null);
|
||||||
|
final TetheringInterface wifiIfaceWithConfig = new TetheringInterface(
|
||||||
|
TETHERING_WIFI, TEST_WLAN_IFNAME, softApConfig);
|
||||||
|
|
||||||
|
// Register callback before running any tethering.
|
||||||
|
mTethering.registerTetheringEventCallback(callback);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
callback.expectTetheredClientChanged(Collections.emptyList());
|
||||||
|
callback.expectUpstreamChanged(NULL_NETWORK);
|
||||||
|
callback.expectConfigurationChanged(
|
||||||
|
mTethering.getTetheringConfiguration().toStableParcelable());
|
||||||
|
assertTetherStatesNotNullButEmpty(callback.pollTetherStatesChanged());
|
||||||
|
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
|
||||||
|
UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
|
||||||
|
initTetheringUpstream(upstreamState);
|
||||||
|
|
||||||
|
// Start wifi tethering but don't trigger the link layer event yet.
|
||||||
|
mBinderCallingUid = TEST_CALLER_UID;
|
||||||
|
final TetheringRequest tetheringRequest = new TetheringRequest.Builder(TETHERING_WIFI)
|
||||||
|
.setSoftApConfiguration(softApConfig).build();
|
||||||
|
tetheringRequest.setUid(TEST_CALLER_UID);
|
||||||
|
ResultListener successListener = new ResultListener(TETHER_ERROR_NO_ERROR);
|
||||||
|
when(mWifiManager.startTetheredHotspot(null)).thenReturn(true);
|
||||||
|
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
successListener.assertHasResult();
|
||||||
|
|
||||||
|
// Try starting wifi tethering with various fuzzy-matching requests and verify we get
|
||||||
|
// TETHER_ERROR_DUPLICATE_REQUEST.
|
||||||
|
|
||||||
|
// Different static IP addresses
|
||||||
|
final TetheringRequest differentIpAddr = new TetheringRequest.Builder(TETHERING_WIFI)
|
||||||
|
.setSoftApConfiguration(softApConfig)
|
||||||
|
.setStaticIpv4Addresses(new LinkAddress("192.168.0.123/24"),
|
||||||
|
new LinkAddress("192.168.0.42/24"))
|
||||||
|
.build();
|
||||||
|
differentIpAddr.setUid(TEST_CALLER_UID);
|
||||||
|
ResultListener differentIpAddrListener = new ResultListener(TETHER_ERROR_DUPLICATE_REQUEST);
|
||||||
|
mTethering.startTethering(differentIpAddr, TEST_CALLER_PKG, differentIpAddrListener);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
verify(mWifiManager, times(1)).startTetheredHotspot(any());
|
||||||
|
verify(mWifiManager, never()).stopSoftAp();
|
||||||
|
differentIpAddrListener.assertHasResult();
|
||||||
|
|
||||||
|
// Different UID
|
||||||
|
final TetheringRequest differentUid = new TetheringRequest.Builder(TETHERING_WIFI)
|
||||||
|
.setSoftApConfiguration(softApConfig).build();
|
||||||
|
differentUid.setUid(TEST_CALLER_UID + 1);
|
||||||
|
ResultListener differentUidListener = new ResultListener(TETHER_ERROR_DUPLICATE_REQUEST);
|
||||||
|
mTethering.startTethering(differentUid, TEST_CALLER_PKG, differentUidListener);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
differentUidListener.assertHasResult();
|
||||||
|
verify(mWifiManager, times(1)).startTetheredHotspot(any());
|
||||||
|
verify(mWifiManager, never()).stopSoftAp();
|
||||||
|
|
||||||
|
// Mock the link layer event to start IP serving and verify we still get
|
||||||
|
// TETHER_ERROR_DUPLICATE_REQUEST even though the request is no longer pending and is
|
||||||
|
// already serving.
|
||||||
|
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
|
||||||
|
assertArrayEquals(new TetheringInterface[] {wifiIfaceWithoutConfig},
|
||||||
|
callback.pollTetherStatesChanged().availableList);
|
||||||
|
assertArrayEquals(new TetheringInterface[] {wifiIfaceWithConfig},
|
||||||
|
callback.pollTetherStatesChanged().tetheredList);
|
||||||
|
callback.expectUpstreamChanged(upstreamState.network);
|
||||||
|
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STARTED);
|
||||||
|
differentIpAddrListener = new ResultListener(TETHER_ERROR_DUPLICATE_REQUEST);
|
||||||
|
differentUidListener = new ResultListener(TETHER_ERROR_DUPLICATE_REQUEST);
|
||||||
|
mTethering.startTethering(differentIpAddr, TEST_CALLER_PKG, differentIpAddrListener);
|
||||||
|
mTethering.startTethering(differentUid, TEST_CALLER_PKG, differentUidListener);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
differentIpAddrListener.assertHasResult();
|
||||||
|
differentUidListener.assertHasResult();
|
||||||
|
verify(mWifiManager, times(1)).startTetheredHotspot(any());
|
||||||
|
verify(mWifiManager, never()).stopSoftAp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFuzzyMatchedWifiCanBeAddedAfterIpServerStopped() throws Exception {
|
||||||
|
assumeTrue(isTetheringWithSoftApConfigEnabled());
|
||||||
|
when(mWifiManager.startTetheredHotspot(null)).thenReturn(true);
|
||||||
|
initTetheringOnTestThread();
|
||||||
|
|
||||||
|
// Start wifi tethering and mock the ap state change.
|
||||||
|
SoftApConfiguration softApConfig = new SoftApConfiguration.Builder().setWifiSsid(
|
||||||
|
WifiSsid.fromBytes("SoftApConfig".getBytes(StandardCharsets.UTF_8))).build();
|
||||||
|
final TetheringRequest tetheringRequest = new TetheringRequest.Builder(TETHERING_WIFI)
|
||||||
|
.setSoftApConfiguration(softApConfig).build();
|
||||||
|
ResultListener successListener = new ResultListener(TETHER_ERROR_NO_ERROR);
|
||||||
|
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
successListener.assertHasResult();
|
||||||
|
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
|
||||||
|
|
||||||
|
// Starting wifi again will cause TETHER_ERROR_DUPLICATE_REQUEST
|
||||||
|
ResultListener failureListener = new ResultListener(TETHER_ERROR_DUPLICATE_REQUEST);
|
||||||
|
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, failureListener);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
failureListener.assertHasResult();
|
||||||
|
|
||||||
|
// Trigger Netd callback to stop the IpServer
|
||||||
|
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, false);
|
||||||
|
|
||||||
|
// We should be able to request the same Wifi again
|
||||||
|
ResultListener successListener2 = new ResultListener(TETHER_ERROR_DUPLICATE_REQUEST);
|
||||||
|
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener2);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
successListener2.assertHasResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFuzzyMatchedWifiCanBeAddedAfterIpServerUnwanted() throws Exception {
|
||||||
|
assumeTrue(isTetheringWithSoftApConfigEnabled());
|
||||||
|
when(mWifiManager.startTetheredHotspot(null)).thenReturn(true);
|
||||||
|
initTetheringOnTestThread();
|
||||||
|
|
||||||
|
// Start wifi tethering and mock the ap state change.
|
||||||
|
SoftApConfiguration softApConfig = new SoftApConfiguration.Builder().setWifiSsid(
|
||||||
|
WifiSsid.fromBytes("SoftApConfig".getBytes(StandardCharsets.UTF_8))).build();
|
||||||
|
final TetheringRequest tetheringRequest = new TetheringRequest.Builder(TETHERING_WIFI)
|
||||||
|
.setSoftApConfiguration(softApConfig).build();
|
||||||
|
ResultListener successListener = new ResultListener(TETHER_ERROR_NO_ERROR);
|
||||||
|
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
successListener.assertHasResult();
|
||||||
|
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
|
||||||
|
|
||||||
|
// Starting wifi again will cause TETHER_ERROR_DUPLICATE_REQUEST
|
||||||
|
ResultListener failureListener = new ResultListener(TETHER_ERROR_DUPLICATE_REQUEST);
|
||||||
|
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, failureListener);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
failureListener.assertHasResult();
|
||||||
|
|
||||||
|
// Trigger wifi ap state change to tell IpServer it's unwanted.
|
||||||
|
sendWifiApStateChanged(WIFI_AP_STATE_DISABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
|
||||||
|
|
||||||
|
// We should be able to request the same Wifi again
|
||||||
|
ResultListener successListener2 = new ResultListener(TETHER_ERROR_NO_ERROR);
|
||||||
|
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener2);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
successListener2.assertHasResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFuzzyMatchedWifiCanBeAddedAfterIpServerError() throws Exception {
|
||||||
|
assumeTrue(isTetheringWithSoftApConfigEnabled());
|
||||||
|
when(mWifiManager.startTetheredHotspot(null)).thenReturn(true);
|
||||||
|
initTetheringOnTestThread();
|
||||||
|
|
||||||
|
// Set up the DHCP server to fail creation.
|
||||||
|
mIpServerDependencies.setOnDhcpServerCreatedResult(STATUS_UNKNOWN_ERROR);
|
||||||
|
|
||||||
|
// Start wifi tethering and mock the ap state change.
|
||||||
|
SoftApConfiguration softApConfig = new SoftApConfiguration.Builder().setWifiSsid(
|
||||||
|
WifiSsid.fromBytes("SoftApConfig".getBytes(StandardCharsets.UTF_8))).build();
|
||||||
|
final TetheringRequest tetheringRequest = new TetheringRequest.Builder(TETHERING_WIFI)
|
||||||
|
.setSoftApConfiguration(softApConfig).build();
|
||||||
|
ResultListener successListener = new ResultListener(TETHER_ERROR_NO_ERROR);
|
||||||
|
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
successListener.assertHasResult();
|
||||||
|
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
|
||||||
|
|
||||||
|
// We should be able to request the same Wifi again since the DHCP server transitioned the
|
||||||
|
// IpServer back to InitialState.
|
||||||
|
ResultListener successListener2 = new ResultListener(TETHER_ERROR_NO_ERROR);
|
||||||
|
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener2);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
successListener2.assertHasResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFuzzyMatchedWifiCanBeAddedAfterStoppingPendingRequest() throws Exception {
|
||||||
|
assumeTrue(isTetheringWithSoftApConfigEnabled());
|
||||||
|
when(mWifiManager.startTetheredHotspot(null)).thenReturn(true);
|
||||||
|
initTetheringOnTestThread();
|
||||||
|
|
||||||
|
// Start wifi tethering but keep the request pending by not sending the ap state change.
|
||||||
|
SoftApConfiguration softApConfig = new SoftApConfiguration.Builder().setWifiSsid(
|
||||||
|
WifiSsid.fromBytes("SoftApConfig".getBytes(StandardCharsets.UTF_8))).build();
|
||||||
|
final TetheringRequest tetheringRequest = new TetheringRequest.Builder(TETHERING_WIFI)
|
||||||
|
.setSoftApConfiguration(softApConfig).build();
|
||||||
|
ResultListener successListener = new ResultListener(TETHER_ERROR_NO_ERROR);
|
||||||
|
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
successListener.assertHasResult();
|
||||||
|
|
||||||
|
// Starting wifi again will cause TETHER_ERROR_DUPLICATE_REQUEST
|
||||||
|
ResultListener failureListener = new ResultListener(TETHER_ERROR_DUPLICATE_REQUEST);
|
||||||
|
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, failureListener);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
failureListener.assertHasResult();
|
||||||
|
|
||||||
|
// Stop Wifi tethering.
|
||||||
|
mTethering.stopTethering(TETHERING_WIFI);
|
||||||
|
|
||||||
|
// We should be able to request the same Wifi again
|
||||||
|
ResultListener successListener2 = new ResultListener(TETHER_ERROR_NO_ERROR);
|
||||||
|
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener2);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
successListener2.assertHasResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFuzzyMatchedWifiCanBeAddedAfterStoppingServingRequest() throws Exception {
|
||||||
|
assumeTrue(isTetheringWithSoftApConfigEnabled());
|
||||||
|
when(mWifiManager.startTetheredHotspot(null)).thenReturn(true);
|
||||||
|
initTetheringOnTestThread();
|
||||||
|
|
||||||
|
// Start wifi tethering and mock the ap state change.
|
||||||
|
SoftApConfiguration softApConfig = new SoftApConfiguration.Builder().setWifiSsid(
|
||||||
|
WifiSsid.fromBytes("SoftApConfig".getBytes(StandardCharsets.UTF_8))).build();
|
||||||
|
final TetheringRequest tetheringRequest = new TetheringRequest.Builder(TETHERING_WIFI)
|
||||||
|
.setSoftApConfiguration(softApConfig).build();
|
||||||
|
ResultListener successListener = new ResultListener(TETHER_ERROR_NO_ERROR);
|
||||||
|
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
successListener.assertHasResult();
|
||||||
|
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
|
||||||
|
|
||||||
|
// Starting wifi again will cause TETHER_ERROR_DUPLICATE_REQUEST
|
||||||
|
ResultListener failureListener = new ResultListener(TETHER_ERROR_DUPLICATE_REQUEST);
|
||||||
|
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, failureListener);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
failureListener.assertHasResult();
|
||||||
|
|
||||||
|
// Stop Wifi tethering.
|
||||||
|
mTethering.stopTethering(TETHERING_WIFI);
|
||||||
|
|
||||||
|
// We should be able to request the same Wifi again
|
||||||
|
ResultListener successListener2 = new ResultListener(TETHER_ERROR_NO_ERROR);
|
||||||
|
mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, successListener2);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
successListener2.assertHasResult();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReportFailCallbackIfOffloadNotSupported() throws Exception {
|
public void testReportFailCallbackIfOffloadNotSupported() throws Exception {
|
||||||
initTetheringOnTestThread();
|
initTetheringOnTestThread();
|
||||||
|
|
@ -2794,7 +3045,8 @@ public class TetheringTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMultipleStartTethering() throws Exception {
|
public void testMultipleStartTetheringLegacy() throws Exception {
|
||||||
|
assumeFalse(isTetheringWithSoftApConfigEnabled());
|
||||||
initTetheringOnTestThread();
|
initTetheringOnTestThread();
|
||||||
final LinkAddress serverLinkAddr = new LinkAddress("192.168.20.1/24");
|
final LinkAddress serverLinkAddr = new LinkAddress("192.168.20.1/24");
|
||||||
final LinkAddress clientLinkAddr = new LinkAddress("192.168.20.42/24");
|
final LinkAddress clientLinkAddr = new LinkAddress("192.168.20.42/24");
|
||||||
|
|
@ -3575,6 +3827,7 @@ public class TetheringTest {
|
||||||
@Test
|
@Test
|
||||||
public void testStartBluetoothTetheringFailsWhenTheresAnExistingRequestWaitingForPanService()
|
public void testStartBluetoothTetheringFailsWhenTheresAnExistingRequestWaitingForPanService()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
assumeFalse(isTetheringWithSoftApConfigEnabled());
|
||||||
initTetheringOnTestThread();
|
initTetheringOnTestThread();
|
||||||
|
|
||||||
mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
|
mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import static android.net.TetheringManager.TETHERING_USB;
|
||||||
import static android.net.TetheringManager.TETHERING_VIRTUAL;
|
import static android.net.TetheringManager.TETHERING_VIRTUAL;
|
||||||
import static android.net.TetheringManager.TETHERING_WIFI;
|
import static android.net.TetheringManager.TETHERING_WIFI;
|
||||||
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
|
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
|
||||||
|
import static android.net.TetheringManager.TETHER_ERROR_DUPLICATE_REQUEST;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
|
import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
|
import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
|
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
|
||||||
|
|
@ -234,6 +235,30 @@ public class TetheringManagerTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartTetheringDuplicateRequestRejected() throws Exception {
|
||||||
|
assumeTrue(isTetheringWithSoftApConfigEnabled());
|
||||||
|
final TestTetheringEventCallback tetherEventCallback =
|
||||||
|
mCtsTetheringUtils.registerTetheringEventCallback();
|
||||||
|
try {
|
||||||
|
tetherEventCallback.assumeWifiTetheringSupported(mContext);
|
||||||
|
tetherEventCallback.expectNoTetheringActive();
|
||||||
|
|
||||||
|
final String[] wifiRegexs = mTM.getTetherableWifiRegexs();
|
||||||
|
mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
|
||||||
|
mTetherChangeReceiver.expectTethering(true /* active */, wifiRegexs);
|
||||||
|
|
||||||
|
final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
|
||||||
|
runAsShell(TETHER_PRIVILEGED, () -> {
|
||||||
|
mTM.startTethering(new TetheringRequest.Builder(TETHERING_WIFI).build(),
|
||||||
|
c -> c.run() /* executor */, startTetheringCallback);
|
||||||
|
startTetheringCallback.expectTetheringFailed(TETHER_ERROR_DUPLICATE_REQUEST);
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private SoftApConfiguration createSoftApConfiguration(@NonNull String ssid) {
|
private SoftApConfiguration createSoftApConfiguration(@NonNull String ssid) {
|
||||||
SoftApConfiguration config;
|
SoftApConfiguration config;
|
||||||
if (SdkLevel.isAtLeastT()) {
|
if (SdkLevel.isAtLeastT()) {
|
||||||
|
|
@ -380,17 +405,20 @@ public class TetheringManagerTest {
|
||||||
tetherEventCallback.assumeWifiTetheringSupported(mContext);
|
tetherEventCallback.assumeWifiTetheringSupported(mContext);
|
||||||
tetherEventCallback.expectNoTetheringActive();
|
tetherEventCallback.expectNoTetheringActive();
|
||||||
|
|
||||||
SoftApConfiguration softApConfig = createSoftApConfiguration("SSID");
|
SoftApConfiguration softApConfig = isTetheringWithSoftApConfigEnabled()
|
||||||
|
? createSoftApConfiguration("SSID") : null;
|
||||||
final TetheringInterface tetheredIface =
|
final TetheringInterface tetheredIface =
|
||||||
mCtsTetheringUtils.startWifiTethering(tetherEventCallback, softApConfig);
|
mCtsTetheringUtils.startWifiTethering(tetherEventCallback, softApConfig);
|
||||||
|
|
||||||
assertNotNull(tetheredIface);
|
assertNotNull(tetheredIface);
|
||||||
assertEquals(softApConfig, tetheredIface.getSoftApConfiguration());
|
if (isTetheringWithSoftApConfigEnabled()) {
|
||||||
final String wifiTetheringIface = tetheredIface.getInterface();
|
assertEquals(softApConfig, tetheredIface.getSoftApConfiguration());
|
||||||
|
}
|
||||||
|
|
||||||
mCtsTetheringUtils.stopWifiTethering(tetherEventCallback);
|
mCtsTetheringUtils.stopWifiTethering(tetherEventCallback);
|
||||||
|
|
||||||
if (!SdkLevel.isAtLeastB()) {
|
if (!SdkLevel.isAtLeastB()) {
|
||||||
|
final String wifiTetheringIface = tetheredIface.getInterface();
|
||||||
try {
|
try {
|
||||||
final int ret = runAsShell(TETHER_PRIVILEGED,
|
final int ret = runAsShell(TETHER_PRIVILEGED,
|
||||||
() -> mTM.tether(wifiTetheringIface));
|
() -> mTM.tether(wifiTetheringIface));
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue