From 01b9e966d8f69424fc582f839d64d44715f04410 Mon Sep 17 00:00:00 2001 From: Quang Anh Luong Date: Mon, 20 Jan 2025 16:40:10 +0900 Subject: [PATCH] Add methods to distinguish TetheringRequest types Add TetheringRequest#RequestType to distinguish the type of TetheringRequest (i.e. from startTethering, from implicit broadcasts, or from the legacy tether API) as well as helper methods to create these requests. These will be used in a future CL. Bug: 216524590 Test: build Change-Id: Ibfda48f59e80d423c9b6ee0d244cbdab14be1bd2 --- .../src/android/net/TetheringManager.java | 59 ++++++++++++++++- .../android/net/TetheringRequestParcel.aidl | 1 + .../networkstack/tethering/Tethering.java | 66 +++++++++++++++---- .../tethering/TetheringService.java | 6 +- .../tethering/TetheringServiceTest.java | 6 +- .../networkstack/tethering/TetheringTest.java | 8 +-- 6 files changed, 124 insertions(+), 22 deletions(-) diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java index 21f36e8531..aa7a244799 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -807,6 +807,46 @@ public class TetheringManager { */ @SuppressLint("UnflaggedApi") public static final class TetheringRequest implements Parcelable { + /** + * Tethering started by an explicit call to startTethering. + * @hide + */ + public static final int REQUEST_TYPE_EXPLICIT = 0; + + /** + * Tethering implicitly started by broadcasts (LOHS and P2P). Can never be pending. + * @hide + */ + public static final int REQUEST_TYPE_IMPLICIT = 1; + + /** + * Tethering started by the legacy tether() call. Can only happen on V-. + * @hide + */ + public static final int REQUEST_TYPE_LEGACY = 2; + + /** + * Tethering started but there was no pending request found. This may happen if Tethering is + * started and immediately stopped before the link layer goes up, or if we get a link layer + * event without a prior call to startTethering (e.g. adb shell cmd wifi start-softap). + * @hide + */ + public static final int REQUEST_TYPE_PLACEHOLDER = 3; + + /** + * Type of request, used to keep track of whether the request was explicitly sent by + * startTethering, implicitly created by broadcasts, or via legacy tether(). + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "TYPE_", value = { + REQUEST_TYPE_EXPLICIT, + REQUEST_TYPE_IMPLICIT, + REQUEST_TYPE_LEGACY, + REQUEST_TYPE_PLACEHOLDER, + }) + public @interface RequestType {} + /** A configuration set for TetheringRequest. */ private final TetheringRequestParcel mRequestParcel; @@ -866,6 +906,7 @@ public class TetheringManager { mBuilderParcel.uid = Process.INVALID_UID; mBuilderParcel.softApConfig = null; mBuilderParcel.interfaceName = null; + mBuilderParcel.requestType = REQUEST_TYPE_EXPLICIT; } /** @@ -1160,6 +1201,14 @@ public class TetheringManager { return mRequestParcel; } + /** + * Get the type of the request. + * @hide + */ + public @RequestType int getRequestType() { + return mRequestParcel.requestType; + } + /** * String of TetheringRequest detail. * @hide @@ -1168,6 +1217,13 @@ public class TetheringManager { public String toString() { StringJoiner sj = new StringJoiner(", ", "TetheringRequest[ ", " ]"); sj.add(typeToString(mRequestParcel.tetheringType)); + if (mRequestParcel.requestType == REQUEST_TYPE_IMPLICIT) { + sj.add("IMPLICIT"); + } else if (mRequestParcel.requestType == REQUEST_TYPE_LEGACY) { + sj.add("LEGACY"); + } else if (mRequestParcel.requestType == REQUEST_TYPE_PLACEHOLDER) { + sj.add("PLACEHOLDER"); + } if (mRequestParcel.localIPv4Address != null) { sj.add("localIpv4Address=" + mRequestParcel.localIPv4Address); } @@ -1217,7 +1273,8 @@ public class TetheringManager { public boolean equalsIgnoreUidPackage(TetheringRequest otherRequest) { TetheringRequestParcel parcel = getParcel(); TetheringRequestParcel otherParcel = otherRequest.getParcel(); - return parcel.tetheringType == otherParcel.tetheringType + return parcel.requestType == otherParcel.requestType + && parcel.tetheringType == otherParcel.tetheringType && Objects.equals(parcel.localIPv4Address, otherParcel.localIPv4Address) && Objects.equals(parcel.staticClientAddress, otherParcel.staticClientAddress) && parcel.exemptFromEntitlementCheck == otherParcel.exemptFromEntitlementCheck diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl b/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl index 97c9b9a604..9863385f97 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl +++ b/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl @@ -24,6 +24,7 @@ import android.net.wifi.SoftApConfiguration; * @hide */ parcelable TetheringRequestParcel { + int requestType; int tetheringType; LinkAddress localIPv4Address; LinkAddress staticClientAddress; diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index a38862421d..11b14eda88 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -51,6 +51,7 @@ 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; import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED; +import static android.net.TetheringManager.TetheringRequest.REQUEST_TYPE_PLACEHOLDER; import static android.net.TetheringManager.toIfaces; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; @@ -1030,6 +1031,49 @@ 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. + */ + private TetheringRequest createLegacyTetheringRequest(int type, int connectivityScope) { + final TetheringRequest request = new TetheringRequest.Builder(type).build(); + request.getParcel().requestType = TetheringRequest.REQUEST_TYPE_LEGACY; + request.getParcel().connectivityScope = connectivityScope; + 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; + } + + /** + * 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). + */ + @NonNull + private TetheringRequest getOrCreatePendingTetheringRequest(int type) { + TetheringRequest pending = mActiveTetheringRequests.get(type); + if (pending != null) return pending; + + Log.w(TAG, "No pending TetheringRequest for type " + type + " found, creating a placeholder" + + " request"); + TetheringRequest placeholder = new TetheringRequest.Builder(type).build(); + placeholder.getParcel().requestType = REQUEST_TYPE_PLACEHOLDER; + return placeholder; + } + /** * Legacy tether API that starts tethering with CONNECTIVITY_SCOPE_GLOBAL on the given iface. * @@ -1043,7 +1087,7 @@ public class Tethering { * WIFI_(AP/P2P_STATE_CHANGED broadcasts, which makes this API redundant for those types unless * those broadcasts are disabled by OEM. */ - void tether(String iface, int requestedState, final IIntResultListener listener) { + void legacyTether(String iface, int requestedState, final IIntResultListener listener) { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.VANILLA_ICE_CREAM) { // After V, the TetheringManager and ConnectivityManager tether and untether methods // throw UnsupportedOperationException, so this cannot happen in normal use. Ensure @@ -1052,7 +1096,7 @@ public class Tethering { return; } mHandler.post(() -> { - int result = tether(iface, requestedState); + int result = tetherInternal(iface, requestedState); switch (ifaceNameToType(iface)) { case TETHERING_WIFI: TerribleErrorLog.logTerribleError(TetheringStatsLog::write, @@ -1088,7 +1132,7 @@ public class Tethering { }); } - private int tether(String iface, int requestedState) { + private int tetherInternal(String iface, int requestedState) { if (DBG) Log.d(TAG, "Tethering " + iface); TetherState tetherState = mTetherStates.get(iface); if (tetherState == null) { @@ -1114,7 +1158,7 @@ public class Tethering { return TETHER_ERROR_NO_ERROR; } - void untether(String iface, final IIntResultListener listener) { + void legacyUntether(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 // throw UnsupportedOperationException, so this cannot happen in normal use. Ensure @@ -1124,13 +1168,13 @@ public class Tethering { } mHandler.post(() -> { try { - listener.onResult(untether(iface)); + listener.onResult(legacyUntetherInternal(iface)); } catch (RemoteException e) { } }); } - int untether(String iface) { + int legacyUntetherInternal(String iface) { if (DBG) Log.d(TAG, "Untethering " + iface); TetherState tetherState = mTetherStates.get(iface); if (tetherState == null) { @@ -1145,7 +1189,7 @@ public class Tethering { return TETHER_ERROR_NO_ERROR; } - void untetherAll() { + void stopAllTethering() { stopTethering(TETHERING_WIFI); stopTethering(TETHERING_WIFI_P2P); stopTethering(TETHERING_USB); @@ -1315,7 +1359,7 @@ public class Tethering { mLog.log("OBSERVED data saver changed"); handleDataSaverChanged(); } else if (action.equals(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING)) { - untetherAll(); + stopAllTethering(); } } @@ -1479,7 +1523,7 @@ public class Tethering { mDataSaverEnabled = isDataSaverEnabled; if (mDataSaverEnabled) { - untetherAll(); + stopAllTethering(); } } } @@ -1538,7 +1582,7 @@ public class Tethering { mNotificationUpdater.notifyTetheringDisabledByRestriction(); // Untether from all downstreams since tethering is disallowed. - mTethering.untetherAll(); + mTethering.stopAllTethering(); } return true; @@ -1553,7 +1597,7 @@ public class Tethering { private void enableIpServing(int tetheringType, String ifname, int ipServingMode, boolean isNcm) { ensureIpServerStarted(ifname, tetheringType, isNcm); - if (tether(ifname, ipServingMode) != TETHER_ERROR_NO_ERROR) { + if (tetherInternal(ifname, ipServingMode) != TETHER_ERROR_NO_ERROR) { Log.e(TAG, "unable start tethering on iface " + ifname); } } diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java index 0c44a3836c..153b0f700c 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java @@ -111,7 +111,7 @@ public class TetheringService extends Service { IIntResultListener listener) { if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; - mTethering.tether(iface, IpServer.STATE_TETHERED, listener); + mTethering.legacyTether(iface, IpServer.STATE_TETHERED, listener); } @Override @@ -119,7 +119,7 @@ public class TetheringService extends Service { IIntResultListener listener) { if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; - mTethering.untether(iface, listener); + mTethering.legacyUntether(iface, listener); } @Override @@ -200,7 +200,7 @@ public class TetheringService extends Service { if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; try { - mTethering.untetherAll(); + mTethering.stopAllTethering(); listener.onResult(TETHER_ERROR_NO_ERROR); } catch (RemoteException e) { } } diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java index cc802517f5..da9b68ec2c 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java @@ -219,7 +219,7 @@ public final class TetheringServiceTest { mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); verify(mTethering).isTetheringSupported(); verify(mTethering).isTetheringAllowed(); - verify(mTethering).tether(TEST_IFACE_NAME, IpServer.STATE_TETHERED, result); + verify(mTethering).legacyTether(TEST_IFACE_NAME, IpServer.STATE_TETHERED, result); } @Test @@ -267,7 +267,7 @@ public final class TetheringServiceTest { result); verify(mTethering).isTetheringSupported(); verify(mTethering).isTetheringAllowed(); - verify(mTethering).untether(eq(TEST_IFACE_NAME), eq(result)); + verify(mTethering).legacyUntether(eq(TEST_IFACE_NAME), eq(result)); } @Test @@ -661,7 +661,7 @@ public final class TetheringServiceTest { mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); verify(mTethering).isTetheringSupported(); verify(mTethering).isTetheringAllowed(); - verify(mTethering).untetherAll(); + verify(mTethering).stopAllTethering(); result.assertResult(TETHER_ERROR_NO_ERROR); } diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java index 97758cf01e..e50a7fd3db 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -2098,7 +2098,7 @@ public class TetheringTest { verify(mNotificationUpdater, times(expectedInteractionsWithShowNotification)) .notifyTetheringDisabledByRestriction(); - verify(mockTethering, times(expectedInteractionsWithShowNotification)).untetherAll(); + verify(mockTethering, times(expectedInteractionsWithShowNotification)).stopAllTethering(); } @Test @@ -3426,7 +3426,7 @@ public class TetheringTest { mTethering.interfaceStatusChanged(TEST_BT_IFNAME, false); mTethering.interfaceStatusChanged(TEST_BT_IFNAME, true); final ResultListener tetherResult = new ResultListener(TETHER_ERROR_NO_ERROR); - mTethering.tether(TEST_BT_IFNAME, IpServer.STATE_TETHERED, tetherResult); + mTethering.legacyTether(TEST_BT_IFNAME, IpServer.STATE_TETHERED, tetherResult); mLooper.dispatchAll(); tetherResult.assertHasResult(); @@ -3446,7 +3446,7 @@ public class TetheringTest { mTethering.stopTethering(TETHERING_BLUETOOTH); mLooper.dispatchAll(); final ResultListener untetherResult = new ResultListener(TETHER_ERROR_NO_ERROR); - mTethering.untether(TEST_BT_IFNAME, untetherResult); + mTethering.legacyUntether(TEST_BT_IFNAME, untetherResult); mLooper.dispatchAll(); untetherResult.assertHasResult(); verifySetBluetoothTethering(false /* enable */, false /* bindToPanService */); @@ -3476,7 +3476,7 @@ public class TetheringTest { mTethering.interfaceStatusChanged(TEST_BT_IFNAME, false); mTethering.interfaceStatusChanged(TEST_BT_IFNAME, true); final ResultListener tetherResult = new ResultListener(TETHER_ERROR_NO_ERROR); - mTethering.tether(TEST_BT_IFNAME, IpServer.STATE_TETHERED, tetherResult); + mTethering.legacyTether(TEST_BT_IFNAME, IpServer.STATE_TETHERED, tetherResult); mLooper.dispatchAll(); tetherResult.assertHasResult(); }