Merge "no-op: Use helper class to handle pending requests" into main
This commit is contained in:
commit
bab398f08c
6 changed files with 369 additions and 85 deletions
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright (C) 2025 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 com.android.networkstack.tethering;
|
||||
|
||||
import static com.android.networkstack.tethering.util.TetheringUtils.createPlaceholderRequest;
|
||||
|
||||
import android.net.TetheringManager.TetheringRequest;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper class to keep track of tethering requests.
|
||||
* The intended usage of this class is
|
||||
* 1) Add a pending request with {@link #addPendingRequest(TetheringRequest)} before asking the link
|
||||
* layer to start.
|
||||
* 2) When the link layer is up, use {@link #getOrCreatePendingRequest(int)} to get a request to
|
||||
* start IP serving with.
|
||||
* 3) Remove all pending requests with {@link #removeAllPendingRequests(int)}.
|
||||
* 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 {
|
||||
private static final String TAG = RequestTracker.class.getSimpleName();
|
||||
|
||||
private class PendingRequest {
|
||||
@NonNull
|
||||
private final TetheringRequest mTetheringRequest;
|
||||
|
||||
private PendingRequest(@NonNull TetheringRequest tetheringRequest) {
|
||||
mTetheringRequest = tetheringRequest;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
TetheringRequest getTetheringRequest() {
|
||||
return mTetheringRequest;
|
||||
}
|
||||
}
|
||||
|
||||
public enum AddResult {
|
||||
/**
|
||||
* Request was successfully added
|
||||
*/
|
||||
SUCCESS,
|
||||
/**
|
||||
* 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
|
||||
* pending request before trying to add the result again.
|
||||
*/
|
||||
FAILURE_CONFLICTING_PENDING_REQUEST
|
||||
}
|
||||
|
||||
/**
|
||||
* List of pending requests added by {@link #addPendingRequest(TetheringRequest)}. 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<>();
|
||||
|
||||
@VisibleForTesting
|
||||
List<TetheringRequest> getPendingTetheringRequests() {
|
||||
List<TetheringRequest> requests = new ArrayList<>();
|
||||
for (PendingRequest pendingRequest : mPendingRequests) {
|
||||
requests.add(pendingRequest.getTetheringRequest());
|
||||
}
|
||||
return requests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a pending request and listener. The request should be added before asking the link layer
|
||||
* to start, and should be retrieved with {@link #getNextPendingRequest(int)} once the link
|
||||
* layer comes up. The result of the add operation will be returned as an AddResult code.
|
||||
*/
|
||||
public AddResult addPendingRequest(@NonNull final TetheringRequest newRequest) {
|
||||
// Check the existing requests to see if it is OK to add the new request.
|
||||
for (PendingRequest request : mPendingRequests) {
|
||||
TetheringRequest existingRequest = request.getTetheringRequest();
|
||||
if (existingRequest.getTetheringType() != newRequest.getTetheringType()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Can't add request if there's a request of the same type with different
|
||||
// parameters.
|
||||
if (!existingRequest.equalsIgnoreUidPackage(newRequest)) {
|
||||
return AddResult.FAILURE_CONFLICTING_PENDING_REQUEST;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the existing pending request of the same type. We already filter out for
|
||||
// conflicting parameters above, so these would have been equivalent anyway (except for
|
||||
// UID).
|
||||
removeAllPendingRequests(newRequest.getTetheringType());
|
||||
mPendingRequests.add(new PendingRequest(newRequest));
|
||||
return AddResult.SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next pending TetheringRequest of a given type, or creates a placeholder request if
|
||||
* there are none.
|
||||
* Note: There are edge cases where the pending request is absent and we must temporarily
|
||||
* synthesize a placeholder request, such as if stopTethering was called before link
|
||||
* layer went up, or if the link layer goes up without us poking it (e.g. adb shell
|
||||
* cmd wifi start-softap). These placeholder requests only specify the tethering type
|
||||
* and the default connectivity scope.
|
||||
*/
|
||||
@NonNull
|
||||
public TetheringRequest getOrCreatePendingRequest(int type) {
|
||||
TetheringRequest pending = getNextPendingRequest(type);
|
||||
if (pending != null) return pending;
|
||||
|
||||
Log.w(TAG, "No pending TetheringRequest for type " + type + " found, creating a"
|
||||
+ " placeholder request");
|
||||
return createPlaceholderRequest(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #getOrCreatePendingRequest(int)} but returns {@code null} if there's no
|
||||
* pending request found.
|
||||
*
|
||||
* @param type Tethering type of the pending request
|
||||
* @return pending request or {@code null} if there are none.
|
||||
*/
|
||||
@Nullable
|
||||
public TetheringRequest getNextPendingRequest(int type) {
|
||||
for (PendingRequest pendingRequest : mPendingRequests) {
|
||||
TetheringRequest tetheringRequest =
|
||||
pendingRequest.getTetheringRequest();
|
||||
if (tetheringRequest.getTetheringType() == type) return tetheringRequest;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all pending requests of the given tethering type.
|
||||
*
|
||||
* @param type Tethering type
|
||||
*/
|
||||
public void removeAllPendingRequests(int type) {
|
||||
mPendingRequests.removeIf(r -> r.getTetheringRequest().getTetheringType() == type);
|
||||
}
|
||||
}
|
||||
|
|
@ -29,7 +29,6 @@ import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
|
|||
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
|
||||
import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
|
||||
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
|
||||
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
|
||||
import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
|
||||
import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER;
|
||||
import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER;
|
||||
|
|
@ -78,6 +77,9 @@ import static com.android.networkstack.tethering.metrics.TetheringStatsLog.CORE_
|
|||
import static com.android.networkstack.tethering.metrics.TetheringStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI_SUCCESS;
|
||||
import static com.android.networkstack.tethering.metrics.TetheringStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_TETHER_WITH_PLACEHOLDER_REQUEST;
|
||||
import static com.android.networkstack.tethering.util.TetheringMessageBase.BASE_MAIN_SM;
|
||||
import static com.android.networkstack.tethering.util.TetheringUtils.createImplicitLocalOnlyTetheringRequest;
|
||||
import static com.android.networkstack.tethering.util.TetheringUtils.createLegacyGlobalScopeTetheringRequest;
|
||||
import static com.android.networkstack.tethering.util.TetheringUtils.createPlaceholderRequest;
|
||||
|
||||
import android.app.usage.NetworkStatsManager;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
|
|
@ -241,11 +243,7 @@ public class Tethering {
|
|||
private final SharedLog mLog = new SharedLog(TAG);
|
||||
private final RemoteCallbackList<ITetheringEventCallback> mTetheringEventCallbacks =
|
||||
new RemoteCallbackList<>();
|
||||
// Currently active tethering requests per tethering type. Only one of each type can be
|
||||
// requested at a time. After a tethering type is requested, the map keeps tethering parameters
|
||||
// to be used after the interface comes up asynchronously.
|
||||
private final SparseArray<TetheringRequest> mPendingTetheringRequests =
|
||||
new SparseArray<>();
|
||||
private final RequestTracker mRequestTracker;
|
||||
|
||||
private final Context mContext;
|
||||
private final ArrayMap<String, TetherState> mTetherStates;
|
||||
|
|
@ -308,6 +306,7 @@ public class Tethering {
|
|||
mLooper = mDeps.makeTetheringLooper();
|
||||
mNotificationUpdater = mDeps.makeNotificationUpdater(mContext, mLooper);
|
||||
mTetheringMetrics = mDeps.makeTetheringMetrics(mContext);
|
||||
mRequestTracker = new RequestTracker();
|
||||
|
||||
// This is intended to ensrure that if something calls startTethering(bluetooth) just after
|
||||
// bluetooth is enabled. Before onServiceConnected is called, store the calls into this
|
||||
|
|
@ -704,13 +703,13 @@ public class Tethering {
|
|||
final IIntResultListener listener) {
|
||||
mHandler.post(() -> {
|
||||
final int type = request.getTetheringType();
|
||||
final TetheringRequest unfinishedRequest = mPendingTetheringRequests.get(type);
|
||||
// If tethering is already enabled with a different request,
|
||||
// disable before re-enabling.
|
||||
if (unfinishedRequest != null && !unfinishedRequest.equalsIgnoreUidPackage(request)) {
|
||||
stopTetheringInternal(type);
|
||||
RequestTracker.AddResult result = mRequestTracker.addPendingRequest(request);
|
||||
// If tethering is already pending with a conflicting request, stop tethering before
|
||||
// starting.
|
||||
if (result == RequestTracker.AddResult.FAILURE_CONFLICTING_PENDING_REQUEST) {
|
||||
stopTetheringInternal(type); // Also removes the request from the tracker.
|
||||
mRequestTracker.addPendingRequest(request);
|
||||
}
|
||||
mPendingTetheringRequests.put(type, request);
|
||||
|
||||
if (request.isExemptFromEntitlementCheck()) {
|
||||
mEntitlementMgr.setExemptedDownstreamType(type);
|
||||
|
|
@ -730,9 +729,7 @@ public class Tethering {
|
|||
}
|
||||
|
||||
private boolean isTetheringTypePendingOrServing(final int type) {
|
||||
for (int i = 0; i < mPendingTetheringRequests.size(); i++) {
|
||||
if (mPendingTetheringRequests.valueAt(i).getTetheringType() == type) return true;
|
||||
}
|
||||
if (mRequestTracker.getNextPendingRequest(type) != null) return true;
|
||||
for (TetherState state : mTetherStates.values()) {
|
||||
// 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
|
||||
|
|
@ -763,7 +760,7 @@ public class Tethering {
|
|||
}
|
||||
|
||||
void stopTetheringInternal(int type) {
|
||||
mPendingTetheringRequests.remove(type);
|
||||
mRequestTracker.removeAllPendingRequests(type);
|
||||
|
||||
// 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.
|
||||
|
|
@ -821,7 +818,7 @@ public class Tethering {
|
|||
// If changing tethering fail, remove corresponding request
|
||||
// no matter who trigger the start/stop.
|
||||
if (result != TETHER_ERROR_NO_ERROR) {
|
||||
mPendingTetheringRequests.remove(type);
|
||||
mRequestTracker.removeAllPendingRequests(type);
|
||||
mTetheringMetrics.updateErrorCode(type, result);
|
||||
mTetheringMetrics.sendReport(type);
|
||||
}
|
||||
|
|
@ -990,7 +987,7 @@ public class Tethering {
|
|||
if (this != mBluetoothCallback) return;
|
||||
|
||||
final TetheringRequest request =
|
||||
getOrCreatePendingTetheringRequest(TETHERING_BLUETOOTH);
|
||||
mRequestTracker.getOrCreatePendingRequest(TETHERING_BLUETOOTH);
|
||||
enableIpServing(request, iface);
|
||||
mConfiguredBluetoothIface = iface;
|
||||
}
|
||||
|
|
@ -1047,7 +1044,8 @@ public class Tethering {
|
|||
return;
|
||||
}
|
||||
|
||||
final TetheringRequest request = getOrCreatePendingTetheringRequest(TETHERING_ETHERNET);
|
||||
final TetheringRequest request = mRequestTracker.getOrCreatePendingRequest(
|
||||
TETHERING_ETHERNET);
|
||||
enableIpServing(request, iface);
|
||||
mConfiguredEthernetIface = iface;
|
||||
}
|
||||
|
|
@ -1079,61 +1077,6 @@ public class Tethering {
|
|||
return TETHER_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a legacy tethering request for calls to the legacy tether() API, which doesn't take an
|
||||
* explicit request. These are always CONNECTIVITY_SCOPE_GLOBAL, per historical behavior.
|
||||
*/
|
||||
private TetheringRequest createLegacyGlobalScopeTetheringRequest(int type) {
|
||||
final TetheringRequest request = new TetheringRequest.Builder(type).build();
|
||||
request.getParcel().requestType = TetheringRequest.REQUEST_TYPE_LEGACY;
|
||||
request.getParcel().connectivityScope = CONNECTIVITY_SCOPE_GLOBAL;
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a local-only implicit tethering request. This is used for Wifi local-only hotspot and
|
||||
* Wifi P2P, which start tethering based on the WIFI_(AP/P2P)_STATE_CHANGED broadcasts.
|
||||
*/
|
||||
@NonNull
|
||||
private TetheringRequest createImplicitLocalOnlyTetheringRequest(int type) {
|
||||
final TetheringRequest request = new TetheringRequest.Builder(type).build();
|
||||
request.getParcel().requestType = TetheringRequest.REQUEST_TYPE_IMPLICIT;
|
||||
request.getParcel().connectivityScope = CONNECTIVITY_SCOPE_LOCAL;
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a placeholder request. This is used in case we try to find a pending request but there
|
||||
* is none (e.g. stopTethering removed a pending request), or for cases where we only have the
|
||||
* tethering type (e.g. stopTethering(int)).
|
||||
*/
|
||||
@NonNull
|
||||
private TetheringRequest createPlaceholderRequest(int type) {
|
||||
final TetheringRequest request = new TetheringRequest.Builder(type).build();
|
||||
request.getParcel().requestType = TetheringRequest.REQUEST_TYPE_LEGACY;
|
||||
request.getParcel().connectivityScope = CONNECTIVITY_SCOPE_GLOBAL;
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the TetheringRequest that #startTethering was called with but is waiting for the link
|
||||
* layer event to indicate the interface is available to tether.
|
||||
* Note: There are edge cases where the pending request is absent and we must temporarily
|
||||
* synthesize a placeholder request, such as if stopTethering was called before link layer
|
||||
* went up, or if the link layer goes up without us poking it (e.g. adb shell cmd wifi
|
||||
* start-softap). These placeholder requests only specify the tethering type and the
|
||||
* default connectivity scope.
|
||||
*/
|
||||
@NonNull
|
||||
private TetheringRequest getOrCreatePendingTetheringRequest(int type) {
|
||||
TetheringRequest pending = mPendingTetheringRequests.get(type);
|
||||
if (pending != null) return pending;
|
||||
|
||||
Log.w(TAG, "No pending TetheringRequest for type " + type + " found, creating a placeholder"
|
||||
+ " request");
|
||||
return createPlaceholderRequest(type);
|
||||
}
|
||||
|
||||
private void handleLegacyTether(String iface, final IIntResultListener listener) {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
// After V, the TetheringManager and ConnectivityManager tether and untether methods
|
||||
|
|
@ -1151,7 +1094,7 @@ public class Tethering {
|
|||
} catch (RemoteException e) { }
|
||||
}
|
||||
|
||||
TetheringRequest request = getPendingTetheringRequest(type);
|
||||
TetheringRequest request = mRequestTracker.getNextPendingRequest(type);
|
||||
if (request == null) {
|
||||
request = createLegacyGlobalScopeTetheringRequest(type);
|
||||
}
|
||||
|
|
@ -1224,7 +1167,7 @@ public class Tethering {
|
|||
// 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.
|
||||
mPendingTetheringRequests.remove(request.getTetheringType());
|
||||
mRequestTracker.removeAllPendingRequests(request.getTetheringType());
|
||||
tetherState.ipServer.enable(request);
|
||||
if (request.getRequestType() == REQUEST_TYPE_PLACEHOLDER) {
|
||||
TerribleErrorLog.logTerribleError(TetheringStatsLog::write,
|
||||
|
|
@ -1590,8 +1533,8 @@ public class Tethering {
|
|||
}
|
||||
|
||||
@VisibleForTesting
|
||||
SparseArray<TetheringRequest> getPendingTetheringRequests() {
|
||||
return mPendingTetheringRequests;
|
||||
List<TetheringRequest> getPendingTetheringRequests() {
|
||||
return mRequestTracker.getPendingTetheringRequests();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
|
@ -1651,10 +1594,6 @@ public class Tethering {
|
|||
}
|
||||
}
|
||||
|
||||
final TetheringRequest getPendingTetheringRequest(int type) {
|
||||
return mPendingTetheringRequests.get(type, null);
|
||||
}
|
||||
|
||||
private void enableIpServing(@NonNull TetheringRequest request, String ifname) {
|
||||
enableIpServing(request, ifname, false /* isNcm */);
|
||||
}
|
||||
|
|
@ -1742,7 +1681,7 @@ public class Tethering {
|
|||
switch (wifiIpMode) {
|
||||
case IFACE_IP_MODE_TETHERED:
|
||||
type = maybeInferWifiTetheringType(ifname);
|
||||
request = getOrCreatePendingTetheringRequest(type);
|
||||
request = mRequestTracker.getOrCreatePendingRequest(type);
|
||||
// Wifi requests will always have CONNECTIVITY_SCOPE_GLOBAL, because
|
||||
// TetheringRequest.Builder will not allow callers to set CONNECTIVITY_SCOPE_LOCAL
|
||||
// for TETHERING_WIFI. However, if maybeInferWifiTetheringType returns a non-Wifi
|
||||
|
|
@ -1797,7 +1736,7 @@ public class Tethering {
|
|||
return;
|
||||
}
|
||||
|
||||
final TetheringRequest request = getOrCreatePendingTetheringRequest(tetheringType);
|
||||
final TetheringRequest request = mRequestTracker.getOrCreatePendingRequest(tetheringType);
|
||||
if (ifaces != null) {
|
||||
for (String iface : ifaces) {
|
||||
if (ifaceNameToType(iface) == tetheringType) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,11 @@
|
|||
*/
|
||||
package com.android.networkstack.tethering.util;
|
||||
|
||||
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
|
||||
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
|
||||
|
||||
import android.net.TetherStatsParcel;
|
||||
import android.net.TetheringManager.TetheringRequest;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
|
@ -166,4 +170,41 @@ public class TetheringUtils {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a legacy tethering request for calls to the legacy tether() API, which doesn't take an
|
||||
* explicit request. These are always CONNECTIVITY_SCOPE_GLOBAL, per historical behavior.
|
||||
*/
|
||||
@NonNull
|
||||
public static TetheringRequest createLegacyGlobalScopeTetheringRequest(int type) {
|
||||
final TetheringRequest request = new TetheringRequest.Builder(type).build();
|
||||
request.getParcel().requestType = TetheringRequest.REQUEST_TYPE_LEGACY;
|
||||
request.getParcel().connectivityScope = CONNECTIVITY_SCOPE_GLOBAL;
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a local-only implicit tethering request. This is used for Wifi local-only hotspot and
|
||||
* Wifi P2P, which start tethering based on the WIFI_(AP/P2P)_STATE_CHANGED broadcasts.
|
||||
*/
|
||||
@NonNull
|
||||
public static TetheringRequest createImplicitLocalOnlyTetheringRequest(int type) {
|
||||
final TetheringRequest request = new TetheringRequest.Builder(type).build();
|
||||
request.getParcel().requestType = TetheringRequest.REQUEST_TYPE_IMPLICIT;
|
||||
request.getParcel().connectivityScope = CONNECTIVITY_SCOPE_LOCAL;
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a placeholder request. This is used in case we try to find a pending request but there
|
||||
* is none (e.g. stopTethering removed a pending request), or for cases where we only have the
|
||||
* tethering type (e.g. stopTethering(int)).
|
||||
*/
|
||||
@NonNull
|
||||
public static TetheringRequest createPlaceholderRequest(int type) {
|
||||
final TetheringRequest request = new TetheringRequest.Builder(type).build();
|
||||
request.getParcel().requestType = TetheringRequest.REQUEST_TYPE_PLACEHOLDER;
|
||||
request.getParcel().connectivityScope = CONNECTIVITY_SCOPE_GLOBAL;
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ java_defaults {
|
|||
"mockito-target-extended-minus-junit4",
|
||||
"net-tests-utils",
|
||||
"testables",
|
||||
"truth",
|
||||
],
|
||||
// TODO(b/147200698) change sdk_version to module-current and
|
||||
// remove framework-minus-apex, ext, and framework-res
|
||||
|
|
|
|||
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright (C) 2025 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 com.android.networkstack.tethering;
|
||||
|
||||
import static android.net.TetheringManager.TETHERING_USB;
|
||||
import static android.net.TetheringManager.TETHERING_WIFI;
|
||||
|
||||
import static com.android.networkstack.tethering.util.TetheringUtils.createPlaceholderRequest;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.net.TetheringManager.TetheringRequest;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.networkstack.tethering.RequestTracker.AddResult;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class RequestTrackerTest {
|
||||
private RequestTracker mRequestTracker;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mRequestTracker = new RequestTracker();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoRequestsAdded_noPendingRequests() {
|
||||
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_WIFI)).isNull();
|
||||
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_WIFI))
|
||||
.isEqualTo(createPlaceholderRequest(TETHERING_WIFI));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddRequest_successResultAndBecomesNextPending() {
|
||||
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_equalRequestExists_successResultAndBecomesNextPending() {
|
||||
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||
mRequestTracker.addPendingRequest(request);
|
||||
|
||||
final TetheringRequest equalRequest = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||
final AddResult result = mRequestTracker.addPendingRequest(equalRequest);
|
||||
|
||||
assertThat(result).isEqualTo(AddResult.SUCCESS);
|
||||
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_WIFI)).isEqualTo(request);
|
||||
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_WIFI)).isEqualTo(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddRequest_equalButDifferentUidRequest_successResultAndBecomesNextPending() {
|
||||
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||
request.setUid(1000);
|
||||
request.setPackageName("package");
|
||||
final TetheringRequest differentUid = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||
differentUid.setUid(2000);
|
||||
differentUid.setPackageName("package2");
|
||||
mRequestTracker.addPendingRequest(request);
|
||||
|
||||
final AddResult result = mRequestTracker.addPendingRequest(differentUid);
|
||||
|
||||
assertThat(result).isEqualTo(AddResult.SUCCESS);
|
||||
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_WIFI)).isEqualTo(differentUid);
|
||||
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_WIFI))
|
||||
.isEqualTo(differentUid);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddConflictingRequest_returnsFailureConflictingPendingRequest() {
|
||||
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_CONFLICTING_PENDING_REQUEST);
|
||||
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_WIFI)).isEqualTo(request);
|
||||
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_WIFI)).isEqualTo(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAllPendingRequests_noPendingRequestsLeft() {
|
||||
final TetheringRequest firstRequest = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||
firstRequest.setUid(1000);
|
||||
firstRequest.setPackageName("package");
|
||||
mRequestTracker.addPendingRequest(firstRequest);
|
||||
final TetheringRequest secondRequest = new TetheringRequest.Builder(TETHERING_WIFI).build();
|
||||
secondRequest.setUid(2000);
|
||||
secondRequest.setPackageName("package2");
|
||||
mRequestTracker.addPendingRequest(secondRequest);
|
||||
|
||||
mRequestTracker.removeAllPendingRequests(TETHERING_WIFI);
|
||||
|
||||
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_WIFI)).isNull();
|
||||
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_WIFI))
|
||||
.isEqualTo(createPlaceholderRequest(TETHERING_WIFI));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAllPendingRequests_differentTypeExists_doesNotRemoveDifferentType() {
|
||||
final TetheringRequest differentType = new TetheringRequest.Builder(TETHERING_USB).build();
|
||||
mRequestTracker.addPendingRequest(differentType);
|
||||
|
||||
mRequestTracker.removeAllPendingRequests(TETHERING_WIFI);
|
||||
|
||||
assertThat(mRequestTracker.getNextPendingRequest(TETHERING_USB)).isEqualTo(differentType);
|
||||
assertThat(mRequestTracker.getOrCreatePendingRequest(TETHERING_USB))
|
||||
.isEqualTo(differentType);
|
||||
}
|
||||
}
|
||||
|
|
@ -964,7 +964,7 @@ public class TetheringTest {
|
|||
mLooper.dispatchAll();
|
||||
|
||||
assertEquals(1, mTethering.getPendingTetheringRequests().size());
|
||||
assertEquals(request, mTethering.getPendingTetheringRequests().get(TETHERING_USB));
|
||||
assertTrue(mTethering.getPendingTetheringRequests().get(0).equals(request));
|
||||
|
||||
if (mTethering.getTetheringConfiguration().isUsingNcm()) {
|
||||
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NCM);
|
||||
|
|
|
|||
Loading…
Reference in a new issue