Add basic implementation of stopTetheringRequest

Add a basic implementation of stopTetheringRequest that simply stops
tethering for that specific type if it is active, or fail with
TETHER_ERROR_UNKNOWN_REQUEST if not. In the future, we will do fuzzy
matching to ensure the correct request is removed.

Bug: 216524590
Test: atest TetheringManagerTest#testStopTetheringRequest
Change-Id: Ib4a660aacdb83e645e39ceb61b7cda45bb24d89d
This commit is contained in:
Quang Anh Luong 2025-01-30 09:36:51 +09:00
commit 696c164c2d
6 changed files with 95 additions and 12 deletions

View file

@ -18,6 +18,7 @@ package android.net;
import android.net.IIntResultListener;
import android.net.ITetheringEventCallback;
import android.net.TetheringRequestParcel;
import android.net.TetheringManager.TetheringRequest;
import android.os.ResultReceiver;
/** @hide */
@ -37,6 +38,9 @@ oneway interface ITetheringConnector {
void stopTethering(int type, String callerPkg, String callingAttributionTag,
IIntResultListener receiver);
void stopTetheringRequest(in TetheringRequest request, String callerPkg,
String callingAttributionTag, IIntResultListener receiver);
void requestLatestTetheringEntitlementResult(int type, in ResultReceiver receiver,
boolean showEntitlementUi, String callerPkg, String callingAttributionTag);

View file

@ -1417,7 +1417,25 @@ public class TetheringManager {
@FlaggedApi(Flags.FLAG_TETHERING_WITH_SOFT_AP_CONFIG)
public void stopTethering(@NonNull TetheringRequest request,
@NonNull final Executor executor, @NonNull final StopTetheringCallback callback) {
throw new UnsupportedOperationException();
Objects.requireNonNull(request);
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "stopTethering: request=" + request + ", caller=" + callerPkg);
getConnector(c -> c.stopTetheringRequest(request, callerPkg, getAttributionTag(),
new IIntResultListener.Stub() {
@Override
public void onResult(final int resultCode) {
executor.execute(() -> {
if (resultCode == TETHER_ERROR_NO_ERROR) {
callback.onStopTetheringSucceeded();
} else {
callback.onStopTetheringFailed(resultCode);
}
});
}
}));
}
/**

View file

@ -47,6 +47,7 @@ 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_UNAVAIL_IFACE;
import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_REQUEST;
import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_TYPE;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
@ -727,6 +728,37 @@ public class Tethering {
stopTetheringInternal(type);
});
}
private boolean isTetheringTypePendingOrServing(final int type) {
for (int i = 0; i < mPendingTetheringRequests.size(); i++) {
if (mPendingTetheringRequests.valueAt(i).getTetheringType() == type) return true;
}
for (TetherState state : mTetherStates.values()) {
if (state.isCurrentlyServing() && state.ipServer.interfaceType() == type) return true;
}
return false;
}
void stopTetheringRequest(@NonNull final TetheringRequest request,
@NonNull final IIntResultListener listener) {
if (!isTetheringWithSoftApConfigEnabled()) return;
mHandler.post(() -> {
final int type = request.getTetheringType();
if (isTetheringTypePendingOrServing(type)) {
stopTetheringInternal(type);
try {
listener.onResult(TETHER_ERROR_NO_ERROR);
} catch (RemoteException ignored) { }
return;
}
// Request doesn't match any active requests, ignore.
try {
listener.onResult(TETHER_ERROR_UNKNOWN_REQUEST);
} catch (RemoteException ignored) { }
});
}
void stopTetheringInternal(int type) {
mPendingTetheringRequests.remove(type);

View file

@ -158,6 +158,18 @@ public class TetheringService extends Service {
} catch (RemoteException e) { }
}
@Override
public void stopTetheringRequest(TetheringRequest request,
String callerPkg, String callingAttributionTag,
IIntResultListener listener) {
if (request == null) return;
if (listener == null) return;
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
request.setUid(getBinderCallingUid());
request.setPackageName(callerPkg);
mTethering.stopTetheringRequest(request, listener);
}
@Override
public void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver,
boolean showEntitlementUi, String callerPkg, String callingAttributionTag) {

View file

@ -22,6 +22,7 @@ import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.TETHER_PRIVILEGED;
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_REQUEST;
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_STOPPED;
@ -637,17 +638,16 @@ public final class CtsTetheringUtils {
/**
* Calls {@link TetheringManager#stopTethering(TetheringRequest, Executor,
* TetheringManager.StopTetheringCallback)} and verifies it throws an
* UnsupportedOperationException.
* TetheringManager.StopTetheringCallback)} and verifies if it succeeded or failed.
*/
public void stopTethering(final TetheringRequest request) {
final StopTetheringCallback callback = new StopTetheringCallback();
public void stopTethering(final TetheringRequest request, boolean expectSuccess) {
final StopTetheringCallback stopTetheringCallback = new StopTetheringCallback();
runAsShell(TETHER_PRIVILEGED, () -> {
try {
mTm.stopTethering(request, Runnable::run /* Executor */, callback);
fail("stopTethering should throw UnsupportedOperationException");
} catch (UnsupportedOperationException expected) {
// Success.
mTm.stopTethering(request, c -> c.run() /* executor */, stopTetheringCallback);
if (expectSuccess) {
stopTetheringCallback.verifyStopTetheringSucceeded();
} else {
stopTetheringCallback.expectStopTetheringFailed(TETHER_ERROR_UNKNOWN_REQUEST);
}
});
}

View file

@ -458,8 +458,25 @@ public class TetheringManagerTest {
@Test
public void testStopTetheringRequest() throws Exception {
assumeTrue(isTetheringWithSoftApConfigEnabled());
TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
mCtsTetheringUtils.stopTethering(request);
final TestTetheringEventCallback tetherEventCallback =
mCtsTetheringUtils.registerTetheringEventCallback();
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
// stopTethering without any tethering active should fail.
TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
mCtsTetheringUtils.stopTethering(request, false /* succeeded */);
// Start wifi tethering
mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
// stopTethering should succeed now that there's a request.
mCtsTetheringUtils.stopTethering(request, true /* succeeded */);
tetherEventCallback.expectNoTetheringActive();
} finally {
mCtsTetheringUtils.stopAllTethering();
mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
}
}
private boolean isTetheringWithSoftApConfigEnabled() {