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(); }