Merge "Update allowed on restricted networks getter/setter"
diff --git a/OWNERS b/OWNERS
index 0eb94fb..22b5561 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,10 +1,7 @@
-# Owners block: ongoing migration
+codewiz@google.com
+jchalard@google.com
+junyulai@google.com
+lorenzo@google.com
+maze@google.com
reminv@google.com
-
-#codewiz@google.com
-#jchalard@google.com
-#junyulai@google.com
-#lorenzo@google.com
-#maze@google.com
-#reminv@google.com
-#satk@google.com
+satk@google.com
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index 79c2b9d..5a08b23 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -146,7 +146,10 @@
manifest: "AndroidManifest.xml",
use_embedded_native_libs: true,
// The permission configuration *must* be included to ensure security of the device
- required: ["NetworkPermissionConfig"],
+ required: [
+ "NetworkPermissionConfig",
+ "privapp_whitelist_com.android.networkstack.tethering",
+ ],
apex_available: ["com.android.tethering"],
min_sdk_version: "30",
}
diff --git a/Tethering/OWNERS b/Tethering/OWNERS
index 204abbd..5b42d49 100644
--- a/Tethering/OWNERS
+++ b/Tethering/OWNERS
@@ -1,5 +1,2 @@
-# include platform/packages/modules/NetworkStack/:/OWNERS
-
-# Owners block: ongoing migration
-# markchien@google.com
-reminv@google.com
\ No newline at end of file
+include platform/packages/modules/NetworkStack/:/OWNERS
+markchien@google.com
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index ee3edc9..bd7ebda 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -41,6 +41,7 @@
"ServiceConnectivityResources",
"Tethering",
],
+ prebuilts: ["current_sdkinfo"],
manifest: "manifest.json",
key: "com.android.tethering.key",
// Indicates that pre-installed version of this apex can be compressed.
diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp
index e47d802..4bc8f93 100644
--- a/Tethering/common/TetheringLib/Android.bp
+++ b/Tethering/common/TetheringLib/Android.bp
@@ -56,6 +56,8 @@
"src/android/net/TetheringConfigurationParcel.aidl",
"src/android/net/TetheringRequestParcel.aidl",
"src/android/net/TetherStatesParcel.aidl",
+ "src/android/net/TetheringInterface.aidl",
+ "src/android/net/TetheringInterface.java",
],
path: "src"
}
diff --git a/Tethering/common/TetheringLib/api/module-lib-current.txt b/Tethering/common/TetheringLib/api/module-lib-current.txt
index 6ddb122..0566040 100644
--- a/Tethering/common/TetheringLib/api/module-lib-current.txt
+++ b/Tethering/common/TetheringLib/api/module-lib-current.txt
@@ -28,13 +28,13 @@
}
public static interface TetheringManager.TetheringEventCallback {
- method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
+ method @Deprecated public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
}
- public static class TetheringManager.TetheringInterfaceRegexps {
- method @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
- method @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
- method @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
+ @Deprecated public static class TetheringManager.TetheringInterfaceRegexps {
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
}
}
diff --git a/Tethering/common/TetheringLib/api/system-current.txt b/Tethering/common/TetheringLib/api/system-current.txt
index 105bab1..844ff64 100644
--- a/Tethering/common/TetheringLib/api/system-current.txt
+++ b/Tethering/common/TetheringLib/api/system-current.txt
@@ -19,6 +19,15 @@
field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
}
+ public final class TetheringInterface implements android.os.Parcelable {
+ ctor public TetheringInterface(int, @NonNull String);
+ method public int describeContents();
+ method @NonNull public String getInterface();
+ method public int getType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheringInterface> CREATOR;
+ }
+
public class TetheringManager {
method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
@@ -26,7 +35,7 @@
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
- field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
+ field @Deprecated public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
field public static final int CONNECTIVITY_SCOPE_GLOBAL = 1; // 0x1
field public static final int CONNECTIVITY_SCOPE_LOCAL = 2; // 0x2
field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
@@ -74,10 +83,14 @@
public static interface TetheringManager.TetheringEventCallback {
method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
method public default void onError(@NonNull String, int);
+ method public default void onError(@NonNull android.net.TetheringInterface, int);
method public default void onLocalOnlyInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public default void onLocalOnlyInterfacesChanged(@NonNull java.util.Set<android.net.TetheringInterface>);
method public default void onOffloadStatusChanged(int);
method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public default void onTetherableInterfacesChanged(@NonNull java.util.Set<android.net.TetheringInterface>);
method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public default void onTetheredInterfacesChanged(@NonNull java.util.Set<android.net.TetheringInterface>);
method public default void onTetheringSupported(boolean);
method public default void onUpstreamChanged(@Nullable android.net.Network);
}
diff --git a/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl b/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl
index 3d842b3..43262fb 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl
+++ b/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl
@@ -16,15 +16,17 @@
package android.net;
+import android.net.TetheringInterface;
+
/**
* Status details for tethering downstream interfaces.
* {@hide}
*/
parcelable TetherStatesParcel {
- String[] availableList;
- String[] tetheredList;
- String[] localOnlyList;
- String[] erroredIfaceList;
+ TetheringInterface[] availableList;
+ TetheringInterface[] tetheredList;
+ TetheringInterface[] localOnlyList;
+ TetheringInterface[] erroredIfaceList;
// List of Last error code corresponding to each errored iface in erroredIfaceList. */
// TODO: Improve this as b/143122247.
int[] lastErrorList;
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringInterface.aidl b/Tethering/common/TetheringLib/src/android/net/TetheringInterface.aidl
new file mode 100644
index 0000000..7151984
--- /dev/null
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringInterface.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2021 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 android.net;
+
+@JavaOnlyStableParcelable parcelable TetheringInterface;
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringInterface.java b/Tethering/common/TetheringLib/src/android/net/TetheringInterface.java
new file mode 100644
index 0000000..84cdef1
--- /dev/null
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringInterface.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2021 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 android.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.TetheringManager.TetheringType;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * The mapping of tethering interface and type.
+ * @hide
+ */
+@SystemApi
+public final class TetheringInterface implements Parcelable {
+ private final int mType;
+ private final String mInterface;
+
+ public TetheringInterface(@TetheringType int type, @NonNull String iface) {
+ Objects.requireNonNull(iface);
+ mType = type;
+ mInterface = iface;
+ }
+
+ private TetheringInterface(@NonNull Parcel in) {
+ this(in.readInt(), in.readString());
+ }
+
+ /** Get tethering type. */
+ public int getType() {
+ return mType;
+ }
+
+ /** Get tethering interface. */
+ @NonNull
+ public String getInterface() {
+ return mInterface;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeString(mInterface);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mType, mInterface);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof TetheringInterface)) return false;
+ final TetheringInterface other = (TetheringInterface) obj;
+ return mType == other.mType && mInterface.equals(other.mInterface);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Creator<TetheringInterface> CREATOR = new Creator<TetheringInterface>() {
+ @NonNull
+ @Override
+ public TetheringInterface createFromParcel(@NonNull Parcel in) {
+ return new TetheringInterface(in);
+ }
+
+ @NonNull
+ @Override
+ public TetheringInterface[] newArray(int size) {
+ return new TetheringInterface[size];
+ }
+ };
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "TetheringInterface {mType=" + mType
+ + ", mInterface=" + mInterface + "}";
+ }
+}
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index c64da8a..73ab908 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -30,6 +30,7 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -43,6 +44,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
@@ -83,7 +85,10 @@
* {@code TetheringManager.EXTRA_ERRORED_TETHER} to indicate
* the current state of tethering. Each include a list of
* interface names in that state (may be empty).
+ *
+ * @deprecated New client should use TetheringEventCallback instead.
*/
+ @Deprecated
public static final String ACTION_TETHER_STATE_CHANGED =
"android.net.conn.TETHER_STATE_CHANGED";
@@ -531,7 +536,7 @@
/**
* Attempt to both alter the mode of USB and Tethering of USB.
*
- * @deprecated New client should not use this API anymore. All clients should use
+ * @deprecated New clients should not use this API anymore. All clients should use
* #startTethering or #stopTethering which encapsulate proper entitlement logic. If the API is
* used and an entitlement check is needed, downstream USB tethering will be enabled but will
* not have any upstream.
@@ -978,8 +983,13 @@
* multiple times later upon changes.
* @param reg The new regular expressions.
*
+ * @deprecated New clients should use the callbacks with {@link TetheringInterface} which
+ * has the mapping between tethering type and interface. InterfaceRegex is no longer needed
+ * to determine the mapping of tethering type and interface.
+ *
* @hide
*/
+ @Deprecated
@SystemApi(client = MODULE_LIBRARIES)
default void onTetherableInterfaceRegexpsChanged(@NonNull TetheringInterfaceRegexps reg) {}
@@ -994,15 +1004,43 @@
default void onTetherableInterfacesChanged(@NonNull List<String> interfaces) {}
/**
+ * Called when there was a change in the list of tetherable interfaces. Tetherable
+ * interface means this interface is available and can be used for tethering.
+ *
+ * <p>This will be called immediately after the callback is registered, and may be called
+ * multiple times later upon changes.
+ * @param interfaces The set of TetheringInterface of currently tetherable interface.
+ */
+ default void onTetherableInterfacesChanged(@NonNull Set<TetheringInterface> interfaces) {
+ // By default, the new callback calls the old callback, so apps
+ // implementing the old callback just work.
+ onTetherableInterfacesChanged(toIfaces(interfaces));
+ }
+
+ /**
* Called when there was a change in the list of tethered interfaces.
*
* <p>This will be called immediately after the callback is registered, and may be called
* multiple times later upon changes.
- * @param interfaces The list of 0 or more String of currently tethered interface names.
+ * @param interfaces The lit of 0 or more String of currently tethered interface names.
*/
default void onTetheredInterfacesChanged(@NonNull List<String> interfaces) {}
/**
+ * Called when there was a change in the list of tethered interfaces.
+ *
+ * <p>This will be called immediately after the callback is registered, and may be called
+ * multiple times later upon changes.
+ * @param interfaces The set of 0 or more TetheringInterface of currently tethered
+ * interface.
+ */
+ default void onTetheredInterfacesChanged(@NonNull Set<TetheringInterface> interfaces) {
+ // By default, the new callback calls the old callback, so apps
+ // implementing the old callback just work.
+ onTetheredInterfacesChanged(toIfaces(interfaces));
+ }
+
+ /**
* Called when there was a change in the list of local-only interfaces.
*
* <p>This will be called immediately after the callback is registered, and may be called
@@ -1012,6 +1050,20 @@
default void onLocalOnlyInterfacesChanged(@NonNull List<String> interfaces) {}
/**
+ * Called when there was a change in the list of local-only interfaces.
+ *
+ * <p>This will be called immediately after the callback is registered, and may be called
+ * multiple times later upon changes.
+ * @param interfaces The set of 0 or more TetheringInterface of active local-only
+ * interface.
+ */
+ default void onLocalOnlyInterfacesChanged(@NonNull Set<TetheringInterface> interfaces) {
+ // By default, the new callback calls the old callback, so apps
+ // implementing the old callback just work.
+ onLocalOnlyInterfacesChanged(toIfaces(interfaces));
+ }
+
+ /**
* Called when an error occurred configuring tethering.
*
* <p>This will be called immediately after the callback is registered if the latest status
@@ -1022,6 +1074,20 @@
default void onError(@NonNull String ifName, @TetheringIfaceError int error) {}
/**
+ * Called when an error occurred configuring tethering.
+ *
+ * <p>This will be called immediately after the callback is registered if the latest status
+ * on the interface is an error, and may be called multiple times later upon changes.
+ * @param iface The interface that experienced the error.
+ * @param error One of {@code TetheringManager#TETHER_ERROR_*}.
+ */
+ default void onError(@NonNull TetheringInterface iface, @TetheringIfaceError int error) {
+ // By default, the new callback calls the old callback, so apps
+ // implementing the old callback just work.
+ onError(iface.getInterface(), error);
+ }
+
+ /**
* Called when the list of tethered clients changes.
*
* <p>This callback provides best-effort information on connected clients based on state
@@ -1044,9 +1110,37 @@
}
/**
- * Regular expressions used to identify tethering interfaces.
+ * Covert DownStreamInterface collection to interface String array list. Internal use only.
+ *
* @hide
*/
+ public static ArrayList<String> toIfaces(Collection<TetheringInterface> tetherIfaces) {
+ final ArrayList<String> ifaces = new ArrayList<>();
+ for (TetheringInterface tether : tetherIfaces) {
+ ifaces.add(tether.getInterface());
+ }
+
+ return ifaces;
+ }
+
+ private static String[] toIfaces(TetheringInterface[] tetherIfaces) {
+ final String[] ifaces = new String[tetherIfaces.length];
+ for (int i = 0; i < tetherIfaces.length; i++) {
+ ifaces[i] = tetherIfaces[i].getInterface();
+ }
+
+ return ifaces;
+ }
+
+
+ /**
+ * Regular expressions used to identify tethering interfaces.
+ *
+ * @deprecated Instead of using regex to determine tethering type. New client could use the
+ * callbacks with {@link TetheringInterface} which has the mapping of type and interface.
+ * @hide
+ */
+ @Deprecated
@SystemApi(client = MODULE_LIBRARIES)
public static class TetheringInterfaceRegexps {
private final String[] mTetherableBluetoothRegexs;
@@ -1114,10 +1208,10 @@
}
final ITetheringEventCallback remoteCallback = new ITetheringEventCallback.Stub() {
// Only accessed with a lock on this object
- private final HashMap<String, Integer> mErrorStates = new HashMap<>();
- private String[] mLastTetherableInterfaces = null;
- private String[] mLastTetheredInterfaces = null;
- private String[] mLastLocalOnlyInterfaces = null;
+ private final HashMap<TetheringInterface, Integer> mErrorStates = new HashMap<>();
+ private TetheringInterface[] mLastTetherableInterfaces = null;
+ private TetheringInterface[] mLastTetheredInterfaces = null;
+ private TetheringInterface[] mLastLocalOnlyInterfaces = null;
@Override
public void onUpstreamChanged(Network network) throws RemoteException {
@@ -1128,14 +1222,14 @@
private synchronized void sendErrorCallbacks(final TetherStatesParcel newStates) {
for (int i = 0; i < newStates.erroredIfaceList.length; i++) {
- final String iface = newStates.erroredIfaceList[i];
- final Integer lastError = mErrorStates.get(iface);
+ final TetheringInterface tetherIface = newStates.erroredIfaceList[i];
+ final Integer lastError = mErrorStates.get(tetherIface);
final int newError = newStates.lastErrorList[i];
if (newError != TETHER_ERROR_NO_ERROR
&& !Objects.equals(lastError, newError)) {
- callback.onError(iface, newError);
+ callback.onError(tetherIface, newError);
}
- mErrorStates.put(iface, newError);
+ mErrorStates.put(tetherIface, newError);
}
}
@@ -1144,7 +1238,7 @@
if (Arrays.equals(mLastTetherableInterfaces, newStates.availableList)) return;
mLastTetherableInterfaces = newStates.availableList.clone();
callback.onTetherableInterfacesChanged(
- Collections.unmodifiableList(Arrays.asList(mLastTetherableInterfaces)));
+ Collections.unmodifiableSet((new ArraySet(mLastTetherableInterfaces))));
}
private synchronized void maybeSendTetheredIfacesChangedCallback(
@@ -1152,7 +1246,7 @@
if (Arrays.equals(mLastTetheredInterfaces, newStates.tetheredList)) return;
mLastTetheredInterfaces = newStates.tetheredList.clone();
callback.onTetheredInterfacesChanged(
- Collections.unmodifiableList(Arrays.asList(mLastTetheredInterfaces)));
+ Collections.unmodifiableSet((new ArraySet(mLastTetheredInterfaces))));
}
private synchronized void maybeSendLocalOnlyIfacesChangedCallback(
@@ -1160,7 +1254,7 @@
if (Arrays.equals(mLastLocalOnlyInterfaces, newStates.localOnlyList)) return;
mLastLocalOnlyInterfaces = newStates.localOnlyList.clone();
callback.onLocalOnlyInterfacesChanged(
- Collections.unmodifiableList(Arrays.asList(mLastLocalOnlyInterfaces)));
+ Collections.unmodifiableSet((new ArraySet(mLastLocalOnlyInterfaces))));
}
// Called immediately after the callbacks are registered.
@@ -1262,8 +1356,8 @@
if (mTetherStatesParcel == null) return TETHER_ERROR_NO_ERROR;
int i = 0;
- for (String errored : mTetherStatesParcel.erroredIfaceList) {
- if (iface.equals(errored)) return mTetherStatesParcel.lastErrorList[i];
+ for (TetheringInterface errored : mTetherStatesParcel.erroredIfaceList) {
+ if (iface.equals(errored.getInterface())) return mTetherStatesParcel.lastErrorList[i];
i++;
}
@@ -1327,7 +1421,7 @@
mCallback.waitForStarted();
if (mTetherStatesParcel == null) return new String[0];
- return mTetherStatesParcel.availableList;
+ return toIfaces(mTetherStatesParcel.availableList);
}
/**
@@ -1341,7 +1435,7 @@
mCallback.waitForStarted();
if (mTetherStatesParcel == null) return new String[0];
- return mTetherStatesParcel.tetheredList;
+ return toIfaces(mTetherStatesParcel.tetheredList);
}
/**
@@ -1361,7 +1455,7 @@
mCallback.waitForStarted();
if (mTetherStatesParcel == null) return new String[0];
- return mTetherStatesParcel.erroredIfaceList;
+ return toIfaces(mTetherStatesParcel.erroredIfaceList);
}
/**
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 0e8b2b5..7596380 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_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.toIfaces;
import static android.net.util.TetheringMessageBase.BASE_MAIN_SM;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
@@ -92,6 +93,7 @@
import android.net.TetheredClient;
import android.net.TetheringCallbackStartedParcel;
import android.net.TetheringConfigurationParcel;
+import android.net.TetheringInterface;
import android.net.TetheringManager.TetheringRequest;
import android.net.TetheringRequestParcel;
import android.net.ip.IpServer;
@@ -143,7 +145,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
@@ -854,26 +855,27 @@
private void sendTetherStateChangedBroadcast() {
if (!isTetheringSupported()) return;
- final ArrayList<String> availableList = new ArrayList<>();
- final ArrayList<String> tetherList = new ArrayList<>();
- final ArrayList<String> localOnlyList = new ArrayList<>();
- final ArrayList<String> erroredList = new ArrayList<>();
- final ArrayList<Integer> lastErrorList = new ArrayList<>();
+ final ArrayList<TetheringInterface> available = new ArrayList<>();
+ final ArrayList<TetheringInterface> tethered = new ArrayList<>();
+ final ArrayList<TetheringInterface> localOnly = new ArrayList<>();
+ final ArrayList<TetheringInterface> errored = new ArrayList<>();
+ final ArrayList<Integer> lastErrors = new ArrayList<>();
final TetheringConfiguration cfg = mConfig;
- mTetherStatesParcel = new TetherStatesParcel();
int downstreamTypesMask = DOWNSTREAM_NONE;
for (int i = 0; i < mTetherStates.size(); i++) {
- TetherState tetherState = mTetherStates.valueAt(i);
- String iface = mTetherStates.keyAt(i);
+ final TetherState tetherState = mTetherStates.valueAt(i);
+ final int type = tetherState.ipServer.interfaceType();
+ final String iface = mTetherStates.keyAt(i);
+ final TetheringInterface tetheringIface = new TetheringInterface(type, iface);
if (tetherState.lastError != TETHER_ERROR_NO_ERROR) {
- erroredList.add(iface);
- lastErrorList.add(tetherState.lastError);
+ errored.add(tetheringIface);
+ lastErrors.add(tetherState.lastError);
} else if (tetherState.lastState == IpServer.STATE_AVAILABLE) {
- availableList.add(iface);
+ available.add(tetheringIface);
} else if (tetherState.lastState == IpServer.STATE_LOCAL_ONLY) {
- localOnlyList.add(iface);
+ localOnly.add(tetheringIface);
} else if (tetherState.lastState == IpServer.STATE_TETHERED) {
if (cfg.isUsb(iface)) {
downstreamTypesMask |= (1 << TETHERING_USB);
@@ -882,40 +884,63 @@
} else if (cfg.isBluetooth(iface)) {
downstreamTypesMask |= (1 << TETHERING_BLUETOOTH);
}
- tetherList.add(iface);
+ tethered.add(tetheringIface);
}
}
- mTetherStatesParcel.availableList = availableList.toArray(new String[0]);
- mTetherStatesParcel.tetheredList = tetherList.toArray(new String[0]);
- mTetherStatesParcel.localOnlyList = localOnlyList.toArray(new String[0]);
- mTetherStatesParcel.erroredIfaceList = erroredList.toArray(new String[0]);
- mTetherStatesParcel.lastErrorList = new int[lastErrorList.size()];
- Iterator<Integer> iterator = lastErrorList.iterator();
- for (int i = 0; i < lastErrorList.size(); i++) {
- mTetherStatesParcel.lastErrorList[i] = iterator.next().intValue();
- }
+ mTetherStatesParcel = buildTetherStatesParcel(available, localOnly, tethered, errored,
+ lastErrors);
reportTetherStateChanged(mTetherStatesParcel);
- final Intent bcast = new Intent(ACTION_TETHER_STATE_CHANGED);
- bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- bcast.putStringArrayListExtra(EXTRA_AVAILABLE_TETHER, availableList);
- bcast.putStringArrayListExtra(EXTRA_ACTIVE_LOCAL_ONLY, localOnlyList);
- bcast.putStringArrayListExtra(EXTRA_ACTIVE_TETHER, tetherList);
- bcast.putStringArrayListExtra(EXTRA_ERRORED_TETHER, erroredList);
- mContext.sendStickyBroadcastAsUser(bcast, UserHandle.ALL);
+ mContext.sendStickyBroadcastAsUser(buildStateChangeIntent(available, localOnly, tethered,
+ errored), UserHandle.ALL);
if (DBG) {
Log.d(TAG, String.format(
- "sendTetherStateChangedBroadcast %s=[%s] %s=[%s] %s=[%s] %s=[%s]",
- "avail", TextUtils.join(",", availableList),
- "local_only", TextUtils.join(",", localOnlyList),
- "tether", TextUtils.join(",", tetherList),
- "error", TextUtils.join(",", erroredList)));
+ "reportTetherStateChanged %s=[%s] %s=[%s] %s=[%s] %s=[%s]",
+ "avail", TextUtils.join(",", available),
+ "local_only", TextUtils.join(",", localOnly),
+ "tether", TextUtils.join(",", tethered),
+ "error", TextUtils.join(",", errored)));
}
mNotificationUpdater.onDownstreamChanged(downstreamTypesMask);
}
+ private TetherStatesParcel buildTetherStatesParcel(
+ final ArrayList<TetheringInterface> available,
+ final ArrayList<TetheringInterface> localOnly,
+ final ArrayList<TetheringInterface> tethered,
+ final ArrayList<TetheringInterface> errored,
+ final ArrayList<Integer> lastErrors) {
+ final TetherStatesParcel parcel = new TetherStatesParcel();
+
+ parcel.availableList = available.toArray(new TetheringInterface[0]);
+ parcel.tetheredList = tethered.toArray(new TetheringInterface[0]);
+ parcel.localOnlyList = localOnly.toArray(new TetheringInterface[0]);
+ parcel.erroredIfaceList = errored.toArray(new TetheringInterface[0]);
+ parcel.lastErrorList = new int[lastErrors.size()];
+ for (int i = 0; i < lastErrors.size(); i++) {
+ parcel.lastErrorList[i] = lastErrors.get(i);
+ }
+
+ return parcel;
+ }
+
+ private Intent buildStateChangeIntent(final ArrayList<TetheringInterface> available,
+ final ArrayList<TetheringInterface> localOnly,
+ final ArrayList<TetheringInterface> tethered,
+ final ArrayList<TetheringInterface> errored) {
+ final Intent bcast = new Intent(ACTION_TETHER_STATE_CHANGED);
+ bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+
+ bcast.putStringArrayListExtra(EXTRA_AVAILABLE_TETHER, toIfaces(available));
+ bcast.putStringArrayListExtra(EXTRA_ACTIVE_LOCAL_ONLY, toIfaces(localOnly));
+ bcast.putStringArrayListExtra(EXTRA_ACTIVE_TETHER, toIfaces(tethered));
+ bcast.putStringArrayListExtra(EXTRA_ERRORED_TETHER, toIfaces(errored));
+
+ return bcast;
+ }
+
private class StateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context content, Intent intent) {
@@ -2082,10 +2107,10 @@
private TetherStatesParcel emptyTetherStatesParcel() {
final TetherStatesParcel parcel = new TetherStatesParcel();
- parcel.availableList = new String[0];
- parcel.tetheredList = new String[0];
- parcel.localOnlyList = new String[0];
- parcel.erroredIfaceList = new String[0];
+ parcel.availableList = new TetheringInterface[0];
+ parcel.tetheredList = new TetheringInterface[0];
+ parcel.localOnlyList = new TetheringInterface[0];
+ parcel.erroredIfaceList = new TetheringInterface[0];
parcel.lastErrorList = new int[0];
return parcel;
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index de94cba..f1ddc6d 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -80,6 +80,7 @@
import java.util.Collection;
import java.util.List;
import java.util.Random;
+import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -349,7 +350,7 @@
private final CountDownLatch mLocalOnlyStartedLatch = new CountDownLatch(1);
private final CountDownLatch mLocalOnlyStoppedLatch = new CountDownLatch(1);
private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1);
- private final String mIface;
+ private final TetheringInterface mIface;
private volatile boolean mInterfaceWasTethered = false;
private volatile boolean mInterfaceWasLocalOnly = false;
@@ -358,20 +359,24 @@
MyTetheringEventCallback(TetheringManager tm, String iface) {
mTm = tm;
- mIface = iface;
+ mIface = new TetheringInterface(TETHERING_ETHERNET, iface);
}
public void unregister() {
mTm.unregisterTetheringEventCallback(this);
mUnregistered = true;
}
-
@Override
public void onTetheredInterfacesChanged(List<String> interfaces) {
+ fail("Should only call callback that takes a Set<TetheringInterface>");
+ }
+
+ @Override
+ public void onTetheredInterfacesChanged(Set<TetheringInterface> interfaces) {
// Ignore stale callbacks registered by previous test cases.
if (mUnregistered) return;
- if (!mInterfaceWasTethered && (mIface == null || interfaces.contains(mIface))) {
+ if (!mInterfaceWasTethered && interfaces.contains(mIface)) {
// This interface is being tethered for the first time.
Log.d(TAG, "Tethering started: " + interfaces);
mInterfaceWasTethered = true;
@@ -384,10 +389,15 @@
@Override
public void onLocalOnlyInterfacesChanged(List<String> interfaces) {
+ fail("Should only call callback that takes a Set<TetheringInterface>");
+ }
+
+ @Override
+ public void onLocalOnlyInterfacesChanged(Set<TetheringInterface> interfaces) {
// Ignore stale callbacks registered by previous test cases.
if (mUnregistered) return;
- if (!mInterfaceWasLocalOnly && (mIface == null || interfaces.contains(mIface))) {
+ if (!mInterfaceWasLocalOnly && interfaces.contains(mIface)) {
// This interface is being put into local-only mode for the first time.
Log.d(TAG, "Local-only started: " + interfaces);
mInterfaceWasLocalOnly = true;
diff --git a/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java b/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
index 7ffe37a..07aab63 100644
--- a/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
+++ b/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
@@ -15,17 +15,18 @@
*/
package android.tethering.mts;
+import static android.Manifest.permission.ACCESS_WIFI_STATE;
import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.READ_DEVICE_CONFIG;
import static android.Manifest.permission.TETHER_PRIVILEGED;
import static android.Manifest.permission.WRITE_SETTINGS;
+import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.cts.util.CtsTetheringUtils.isWifiTetheringSupported;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
@@ -35,6 +36,7 @@
import android.content.Context;
import android.net.IpPrefix;
import android.net.LinkAddress;
+import android.net.TetheringInterface;
import android.net.TetheringManager;
import android.net.cts.util.CtsTetheringUtils;
import android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback;
@@ -69,7 +71,7 @@
@Before
public void setUp() throws Exception {
mUiAutomation.adoptShellPermissionIdentity(MANAGE_TEST_NETWORKS, NETWORK_SETTINGS,
- WRITE_SETTINGS, READ_DEVICE_CONFIG, TETHER_PRIVILEGED);
+ WRITE_SETTINGS, READ_DEVICE_CONFIG, TETHER_PRIVILEGED, ACCESS_WIFI_STATE);
mContext = InstrumentationRegistry.getContext();
mTm = mContext.getSystemService(TetheringManager.class);
mCtsTetheringUtils = new CtsTetheringUtils(mContext);
@@ -101,13 +103,14 @@
TestNetworkTracker tnt = null;
try {
tetherEventCallback.assumeTetheringSupported();
- assumeTrue(isWifiTetheringSupported(tetherEventCallback));
+ assumeTrue(isWifiTetheringSupported(mContext, tetherEventCallback));
+ tetherEventCallback.expectNoTetheringActive();
- mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
+ final TetheringInterface tetheredIface =
+ mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
- final List<String> tetheredIfaces = tetherEventCallback.getTetheredInterfaces();
- assertEquals(1, tetheredIfaces.size());
- final String wifiTetheringIface = tetheredIfaces.get(0);
+ assertNotNull(tetheredIface);
+ final String wifiTetheringIface = tetheredIface.getInterface();
NetworkInterface nif = NetworkInterface.getByName(wifiTetheringIface);
// Tethering downstream only have one ipv4 address.
@@ -120,11 +123,11 @@
tnt = setUpTestNetwork(
new LinkAddress(testPrefix.getAddress(), testPrefix.getPrefixLength()));
- tetherEventCallback.expectTetheredInterfacesChanged(null);
+ tetherEventCallback.expectNoTetheringActive();
final List<String> wifiRegexs =
tetherEventCallback.getTetheringInterfaceRegexps().getTetherableWifiRegexs();
- tetherEventCallback.expectTetheredInterfacesChanged(wifiRegexs);
+ tetherEventCallback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI);
nif = NetworkInterface.getByName(wifiTetheringIface);
final LinkAddress newHotspotAddr = getFirstIpv4Address(nif);
assertNotNull(newHotspotAddr);
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 94a9238..5235eb7 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -130,6 +130,7 @@
import android.net.TetheredClient.AddressInfo;
import android.net.TetheringCallbackStartedParcel;
import android.net.TetheringConfigurationParcel;
+import android.net.TetheringInterface;
import android.net.TetheringRequestParcel;
import android.net.dhcp.DhcpLeaseParcelable;
import android.net.dhcp.DhcpServerCallbacks;
@@ -1793,6 +1794,8 @@
public void testRegisterTetheringEventCallback() throws Exception {
TestTetheringEventCallback callback = new TestTetheringEventCallback();
TestTetheringEventCallback callback2 = new TestTetheringEventCallback();
+ final TetheringInterface wifiIface = new TetheringInterface(
+ TETHERING_WIFI, TEST_WLAN_IFNAME);
// 1. Register one callback before running any tethering.
mTethering.registerTetheringEventCallback(callback);
@@ -1811,12 +1814,12 @@
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
mLooper.dispatchAll();
tetherState = callback.pollTetherStatesChanged();
- assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME});
+ assertArrayEquals(tetherState.availableList, new TetheringInterface[] {wifiIface});
mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
tetherState = callback.pollTetherStatesChanged();
- assertArrayEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME});
+ assertArrayEquals(tetherState.tetheredList, new TetheringInterface[] {wifiIface});
callback.expectUpstreamChanged(upstreamState.network);
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STARTED);
@@ -1828,7 +1831,7 @@
callback2.expectConfigurationChanged(
mTethering.getTetheringConfiguration().toStableParcelable());
tetherState = callback2.pollTetherStatesChanged();
- assertEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME});
+ assertEquals(tetherState.tetheredList, new TetheringInterface[] {wifiIface});
callback2.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STARTED);
// 4. Unregister first callback and disable wifi tethering
@@ -1837,7 +1840,7 @@
mTethering.stopTethering(TETHERING_WIFI);
sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
tetherState = callback2.pollTetherStatesChanged();
- assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME});
+ assertArrayEquals(tetherState.availableList, new TetheringInterface[] {wifiIface});
mLooper.dispatchAll();
callback2.expectUpstreamChanged(new Network[] {null});
callback2.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
diff --git a/framework/Android.bp b/framework/Android.bp
index c91017f..4fa9ccb 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -108,10 +108,8 @@
"-Wthread-safety",
],
shared_libs: [
- "libbase",
"liblog",
"libnativehelper",
- "libnetd_client",
],
header_libs: [
"dnsproxyd_protocol_headers",
@@ -124,6 +122,7 @@
srcs: [
"jni/android_net_NetworkUtils.cpp",
],
+ shared_libs: ["libandroid_net"],
apex_available: [
"//apex_available:platform",
"com.android.tethering",
@@ -134,9 +133,11 @@
name: "libframework-connectivity-jni",
defaults: ["libframework-connectivity-defaults"],
srcs: [
+ "jni/android_net_NetworkUtils.cpp",
"jni/onload.cpp",
],
- static_libs: ["libconnectivityframeworkutils"],
+ shared_libs: ["libandroid"],
+ stl: "libc++_static",
apex_available: [
"//apex_available:platform",
"com.android.tethering",
diff --git a/framework/jni/android_net_NetworkUtils.cpp b/framework/jni/android_net_NetworkUtils.cpp
index f17baf9..26484fb 100644
--- a/framework/jni/android_net_NetworkUtils.cpp
+++ b/framework/jni/android_net_NetworkUtils.cpp
@@ -19,6 +19,7 @@
#include <vector>
#include <android/file_descriptor_jni.h>
+#include <android/multinetwork.h>
#include <arpa/inet.h>
#include <linux/filter.h>
#include <linux/if_arp.h>
@@ -36,7 +37,6 @@
#include <utils/Log.h>
#include <utils/misc.h>
-#include "NetdClient.h"
#include "jni.h"
extern "C" {
@@ -94,25 +94,32 @@
}
}
-static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
+static jboolean android_net_utils_bindProcessToNetworkHandle(JNIEnv *env, jobject thiz,
+ jlong netHandle)
{
- return (jboolean) !setNetworkForProcess(netId);
+ return (jboolean) !android_setprocnetwork(netHandle);
}
-static jint android_net_utils_getBoundNetworkForProcess(JNIEnv *env, jobject thiz)
+static jlong android_net_utils_getBoundNetworkHandleForProcess(JNIEnv *env, jobject thiz)
{
- return getNetworkForProcess();
+ net_handle_t network;
+ if (android_getprocnetwork(&network) != 0) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "android_getprocnetwork(): %s", strerror(errno));
+ return NETWORK_UNSPECIFIED;
+ }
+ return (jlong) network;
}
static jboolean android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz,
- jint netId)
+ jint netId, jlong netHandle)
{
- return (jboolean) !setNetworkForResolv(netId);
+ return (jboolean) !android_setprocdns(netHandle);
}
-static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jobject javaFd,
- jint netId) {
- return setNetworkForSocket(netId, AFileDescriptor_getFd(env, javaFd));
+static jint android_net_utils_bindSocketToNetworkHandle(JNIEnv *env, jobject thiz, jobject javaFd,
+ jlong netHandle) {
+ return android_setsocknetwork(netHandle, AFileDescriptor_getFd(env, javaFd));
}
static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst)
@@ -124,7 +131,7 @@
return true;
}
-static jobject android_net_utils_resNetworkQuery(JNIEnv *env, jobject thiz, jint netId,
+static jobject android_net_utils_resNetworkQuery(JNIEnv *env, jobject thiz, jlong netHandle,
jstring dname, jint ns_class, jint ns_type, jint flags) {
const jsize javaCharsCount = env->GetStringLength(dname);
const jsize byteCountUTF8 = env->GetStringUTFLength(dname);
@@ -134,7 +141,8 @@
std::vector<char> queryname(byteCountUTF8 + 1, 0);
env->GetStringUTFRegion(dname, 0, javaCharsCount, queryname.data());
- int fd = resNetworkQuery(netId, queryname.data(), ns_class, ns_type, flags);
+
+ int fd = android_res_nquery(netHandle, queryname.data(), ns_class, ns_type, flags);
if (fd < 0) {
jniThrowErrnoException(env, "resNetworkQuery", -fd);
@@ -144,12 +152,12 @@
return jniCreateFileDescriptor(env, fd);
}
-static jobject android_net_utils_resNetworkSend(JNIEnv *env, jobject thiz, jint netId,
+static jobject android_net_utils_resNetworkSend(JNIEnv *env, jobject thiz, jlong netHandle,
jbyteArray msg, jint msgLen, jint flags) {
uint8_t data[MAXCMDSIZE];
checkLenAndCopy(env, msg, msgLen, data);
- int fd = resNetworkSend(netId, data, msgLen, flags);
+ int fd = android_res_nsend(netHandle, data, msgLen, flags);
if (fd < 0) {
jniThrowErrnoException(env, "resNetworkSend", -fd);
@@ -164,7 +172,7 @@
int rcode;
std::vector<uint8_t> buf(MAXPACKETSIZE, 0);
- int res = resNetworkResult(fd, &rcode, buf.data(), MAXPACKETSIZE);
+ int res = android_res_nresult(fd, &rcode, buf.data(), MAXPACKETSIZE);
jniSetFileDescriptorOfFD(env, javaFd, -1);
if (res < 0) {
jniThrowErrnoException(env, "resNetworkResult", -res);
@@ -188,23 +196,22 @@
static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobject javaFd) {
int fd = AFileDescriptor_getFd(env, javaFd);
- resNetworkCancel(fd);
+ android_res_cancel(fd);
jniSetFileDescriptorOfFD(env, javaFd, -1);
}
static jobject android_net_utils_getDnsNetwork(JNIEnv *env, jobject thiz) {
- unsigned dnsNetId = 0;
- if (int res = getNetworkForDns(&dnsNetId) < 0) {
- jniThrowErrnoException(env, "getDnsNetId", -res);
+ net_handle_t dnsNetHandle = NETWORK_UNSPECIFIED;
+ if (int res = android_getprocdns(&dnsNetHandle) < 0) {
+ jniThrowErrnoException(env, "getDnsNetwork", -res);
return nullptr;
}
- bool privateDnsBypass = dnsNetId & NETID_USE_LOCAL_NAMESERVERS;
static jclass class_Network = MakeGlobalRefOrDie(
env, FindClassOrDie(env, "android/net/Network"));
- static jmethodID ctor = env->GetMethodID(class_Network, "<init>", "(IZ)V");
- return env->NewObject(
- class_Network, ctor, dnsNetId & ~NETID_USE_LOCAL_NAMESERVERS, privateDnsBypass);
+ static jmethodID method = env->GetStaticMethodID(class_Network, "fromNetworkHandle",
+ "(J)Landroid/net/Network;");
+ return env->CallStaticObjectMethod(class_Network, method, static_cast<jlong>(dnsNetHandle));
}
static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) {
@@ -250,15 +257,15 @@
// clang-format off
static const JNINativeMethod gNetworkUtilMethods[] = {
/* name, signature, funcPtr */
- { "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
- { "getBoundNetworkForProcess", "()I", (void*) android_net_utils_getBoundNetworkForProcess },
+ { "bindProcessToNetworkHandle", "(J)Z", (void*) android_net_utils_bindProcessToNetworkHandle },
+ { "getBoundNetworkHandleForProcess", "()J", (void*) android_net_utils_getBoundNetworkHandleForProcess },
{ "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
- { "bindSocketToNetwork", "(Ljava/io/FileDescriptor;I)I", (void*) android_net_utils_bindSocketToNetwork },
+ { "bindSocketToNetworkHandle", "(Ljava/io/FileDescriptor;J)I", (void*) android_net_utils_bindSocketToNetworkHandle },
{ "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter },
{ "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter },
{ "getTcpRepairWindow", "(Ljava/io/FileDescriptor;)Landroid/net/TcpRepairWindow;", (void*) android_net_utils_getTcpRepairWindow },
- { "resNetworkSend", "(I[BII)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkSend },
- { "resNetworkQuery", "(ILjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery },
+ { "resNetworkSend", "(J[BII)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkSend },
+ { "resNetworkQuery", "(JLjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery },
{ "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult },
{ "resNetworkCancel", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_resNetworkCancel },
{ "getDnsNetwork", "()Landroid/net/Network;", (void*) android_net_utils_getDnsNetwork },
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 1a6b37b..2af5b35 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -4916,7 +4916,7 @@
InetAddressCompat.clearDnsCache();
// Must flush socket pool as idle sockets will be bound to previous network and may
// cause subsequent fetches to be performed on old network.
- NetworkEventDispatcher.getInstance().onNetworkConfigurationChanged();
+ NetworkEventDispatcher.getInstance().dispatchNetworkConfigurationChange();
}
return true;
diff --git a/framework/src/android/net/Network.java b/framework/src/android/net/Network.java
index 41fad63..1f49033 100644
--- a/framework/src/android/net/Network.java
+++ b/framework/src/android/net/Network.java
@@ -92,6 +92,7 @@
// value in the native/android/net.c NDK implementation.
private static final long HANDLE_MAGIC = 0xcafed00dL;
private static final int HANDLE_MAGIC_SIZE = 32;
+ private static final int USE_LOCAL_NAMESERVERS_FLAG = 0x80000000;
// A boolean to control how getAllByName()/getByName() behaves in the face
// of Private DNS.
@@ -189,7 +190,7 @@
*/
public int getNetIdForResolv() {
return mPrivateDnsBypass
- ? (int) (0x80000000L | (long) netId) // Non-portable DNS resolution flag.
+ ? (USE_LOCAL_NAMESERVERS_FLAG | netId) // Non-portable DNS resolution flag.
: netId;
}
@@ -452,12 +453,13 @@
throw new IllegalArgumentException(
"Network.fromNetworkHandle refusing to instantiate NETID_UNSET Network.");
}
- if ((networkHandle & ((1L << HANDLE_MAGIC_SIZE) - 1)) != HANDLE_MAGIC
- || networkHandle < 0) {
+ if ((networkHandle & ((1L << HANDLE_MAGIC_SIZE) - 1)) != HANDLE_MAGIC) {
throw new IllegalArgumentException(
"Value passed to fromNetworkHandle() is not a network handle.");
}
- return new Network((int) (networkHandle >> HANDLE_MAGIC_SIZE));
+ final int netIdForResolv = (int) (networkHandle >>> HANDLE_MAGIC_SIZE);
+ return new Network((netIdForResolv & ~USE_LOCAL_NAMESERVERS_FLAG),
+ ((netIdForResolv & USE_LOCAL_NAMESERVERS_FLAG) != 0) /* privateDnsBypass */);
}
/**
@@ -485,7 +487,7 @@
if (netId == 0) {
return 0L; // make this zero condition obvious for debugging
}
- return (((long) netId) << HANDLE_MAGIC_SIZE) | HANDLE_MAGIC;
+ return (((long) getNetIdForResolv()) << HANDLE_MAGIC_SIZE) | HANDLE_MAGIC;
}
// implement the Parcelable interface
diff --git a/framework/src/android/net/NetworkUtils.java b/framework/src/android/net/NetworkUtils.java
index a92fda1..16a49bc 100644
--- a/framework/src/android/net/NetworkUtils.java
+++ b/framework/src/android/net/NetworkUtils.java
@@ -16,6 +16,8 @@
package android.net;
+import static android.net.ConnectivityManager.NETID_UNSET;
+
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.system.ErrnoException;
@@ -55,6 +57,8 @@
*/
public static native void detachBPFFilter(FileDescriptor fd) throws SocketException;
+ private static native boolean bindProcessToNetworkHandle(long netHandle);
+
/**
* Binds the current process to the network designated by {@code netId}. All sockets created
* in the future (and not explicitly bound via a bound {@link SocketFactory} (see
@@ -63,13 +67,20 @@
* is by design so an application doesn't accidentally use sockets it thinks are still bound to
* a particular {@code Network}. Passing NETID_UNSET clears the binding.
*/
- public native static boolean bindProcessToNetwork(int netId);
+ public static boolean bindProcessToNetwork(int netId) {
+ return bindProcessToNetworkHandle(new Network(netId).getNetworkHandle());
+ }
+
+ private static native long getBoundNetworkHandleForProcess();
/**
* Return the netId last passed to {@link #bindProcessToNetwork}, or NETID_UNSET if
* {@link #unbindProcessToNetwork} has been called since {@link #bindProcessToNetwork}.
*/
- public native static int getBoundNetworkForProcess();
+ public static int getBoundNetworkForProcess() {
+ final long netHandle = getBoundNetworkHandleForProcess();
+ return netHandle == 0L ? NETID_UNSET : Network.fromNetworkHandle(netHandle).getNetId();
+ }
/**
* Binds host resolutions performed by this process to the network designated by {@code netId}.
@@ -81,12 +92,16 @@
@Deprecated
public native static boolean bindProcessToNetworkForHostResolution(int netId);
+ private static native int bindSocketToNetworkHandle(FileDescriptor fd, long netHandle);
+
/**
* Explicitly binds {@code fd} to the network designated by {@code netId}. This
* overrides any binding via {@link #bindProcessToNetwork}.
* @return 0 on success or negative errno on failure.
*/
- public static native int bindSocketToNetwork(FileDescriptor fd, int netId);
+ public static int bindSocketToNetwork(FileDescriptor fd, int netId) {
+ return bindSocketToNetworkHandle(fd, new Network(netId).getNetworkHandle());
+ }
/**
* Determine if {@code uid} can access network designated by {@code netId}.
@@ -97,14 +112,22 @@
return false;
}
+ private static native FileDescriptor resNetworkSend(
+ long netHandle, byte[] msg, int msglen, int flags) throws ErrnoException;
+
/**
* DNS resolver series jni method.
* Issue the query {@code msg} on the network designated by {@code netId}.
* {@code flags} is an additional config to control actual querying behavior.
* @return a file descriptor to watch for read events
*/
- public static native FileDescriptor resNetworkSend(
- int netId, byte[] msg, int msglen, int flags) throws ErrnoException;
+ public static FileDescriptor resNetworkSend(
+ int netId, byte[] msg, int msglen, int flags) throws ErrnoException {
+ return resNetworkSend(new Network(netId).getNetworkHandle(), msg, msglen, flags);
+ }
+
+ private static native FileDescriptor resNetworkQuery(
+ long netHandle, String dname, int nsClass, int nsType, int flags) throws ErrnoException;
/**
* DNS resolver series jni method.
@@ -113,8 +136,11 @@
* {@code flags} is an additional config to control actual querying behavior.
* @return a file descriptor to watch for read events
*/
- public static native FileDescriptor resNetworkQuery(
- int netId, String dname, int nsClass, int nsType, int flags) throws ErrnoException;
+ public static FileDescriptor resNetworkQuery(
+ int netId, String dname, int nsClass, int nsType, int flags) throws ErrnoException {
+ return resNetworkQuery(new Network(netId).getNetworkHandle(), dname, nsClass, nsType,
+ flags);
+ }
/**
* DNS resolver series jni method.
diff --git a/service/Android.bp b/service/Android.bp
index b6adf79..8c46dea 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -32,6 +32,7 @@
"jni/com_android_server_TestNetworkService.cpp",
"jni/onload.cpp",
],
+ stl: "libc++_static",
shared_libs: [
"libbase",
"liblog",
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 842ad62..adfcace 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -87,7 +87,6 @@
import static java.util.Map.Entry;
import android.Manifest;
-import android.annotation.BoolRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
@@ -150,7 +149,6 @@
import android.net.NetworkScore;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
-import android.net.NetworkStackClient;
import android.net.NetworkState;
import android.net.NetworkStateSnapshot;
import android.net.NetworkTestResultParcelable;
@@ -177,13 +175,14 @@
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
import android.net.netlink.InetDiagMessage;
+import android.net.networkstack.ModuleNetworkStackClient;
+import android.net.networkstack.NetworkStackClientBase;
import android.net.resolv.aidl.DnsHealthEventParcel;
import android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener;
import android.net.resolv.aidl.Nat64PrefixEventParcel;
import android.net.resolv.aidl.PrivateDnsValidationEventParcel;
import android.net.shared.PrivateDnsConfig;
import android.net.util.MultinetworkPolicyTracker;
-import android.net.util.NetdService;
import android.os.BatteryStatsManager;
import android.os.Binder;
import android.os.Build;
@@ -1184,10 +1183,10 @@
}
/**
- * Get a reference to the NetworkStackClient.
+ * Get a reference to the ModuleNetworkStackClient.
*/
- public NetworkStackClient getNetworkStack() {
- return NetworkStackClient.getInstance();
+ public NetworkStackClientBase getNetworkStack() {
+ return ModuleNetworkStackClient.getInstance(null);
}
/**
@@ -1246,7 +1245,8 @@
public ConnectivityService(Context context) {
this(context, getDnsResolver(context), new IpConnectivityLog(),
- NetdService.getInstance(), new Dependencies());
+ INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)),
+ new Dependencies());
}
@VisibleForTesting
@@ -1460,7 +1460,7 @@
mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
}
- private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, @BoolRes int id) {
+ private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, int id) {
final boolean enable = mContext.getResources().getBoolean(id);
handleAlwaysOnNetworkRequest(networkRequest, enable);
}
@@ -2989,10 +2989,6 @@
}
pw.println();
- pw.println("NetworkStackClient logs:");
- pw.increaseIndent();
- NetworkStackClient.getInstance().dump(pw);
- pw.decreaseIndent();
pw.println();
pw.println("Permission Monitor:");
@@ -8777,7 +8773,7 @@
}
}
- private @VpnManager.VpnType int getVpnType(@Nullable NetworkAgentInfo vpn) {
+ private int getVpnType(@Nullable NetworkAgentInfo vpn) {
if (vpn == null) return VpnManager.TYPE_VPN_NONE;
final TransportInfo ti = vpn.networkCapabilities.getTransportInfo();
if (!(ti instanceof VpnTransportInfo)) return VpnManager.TYPE_VPN_NONE;
diff --git a/service/src/com/android/server/TestNetworkService.java b/service/src/com/android/server/TestNetworkService.java
index f566277..09873f4 100644
--- a/service/src/com/android/server/TestNetworkService.java
+++ b/service/src/com/android/server/TestNetworkService.java
@@ -35,7 +35,6 @@
import android.net.RouteInfo;
import android.net.TestNetworkInterface;
import android.net.TestNetworkSpecifier;
-import android.net.util.NetdService;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -86,7 +85,9 @@
mHandler = new Handler(mHandlerThread.getLooper());
mContext = Objects.requireNonNull(context, "missing Context");
- mNetd = Objects.requireNonNull(NetdService.getInstance(), "could not get netd instance");
+ mNetd = Objects.requireNonNull(
+ INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)),
+ "could not get netd instance");
mCm = mContext.getSystemService(ConnectivityManager.class);
mNetworkProvider = new NetworkProvider(mContext, mHandler.getLooper(),
TEST_NETWORK_PROVIDER_NAME);
diff --git a/service/src/com/android/server/connectivity/NetworkNotificationManager.java b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
index 0c0d459..b57ad5d 100644
--- a/service/src/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
@@ -84,7 +84,7 @@
// The context is for the current user (system server)
private final Context mContext;
- private final Resources mResources;
+ private final ConnectivityResources mResources;
private final TelephonyManager mTelephonyManager;
// The notification manager is created from a context for User.ALL, so notifications
// will be sent to all users.
@@ -99,7 +99,7 @@
(NotificationManager) c.createContextAsUser(UserHandle.ALL, 0 /* flags */)
.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationTypeMap = new SparseIntArray();
- mResources = new ConnectivityResources(mContext).get();
+ mResources = new ConnectivityResources(mContext);
}
@VisibleForTesting
@@ -118,11 +118,11 @@
}
private String getTransportName(final int transportType) {
- String[] networkTypes = mResources.getStringArray(R.array.network_switch_type_name);
+ String[] networkTypes = mResources.get().getStringArray(R.array.network_switch_type_name);
try {
return networkTypes[transportType];
} catch (IndexOutOfBoundsException e) {
- return mResources.getString(R.string.network_switch_type_name_unknown);
+ return mResources.get().getString(R.string.network_switch_type_name_unknown);
}
}
@@ -197,10 +197,11 @@
tag, nameOf(eventId), getTransportName(transportType), name, highPriority));
}
- final Resources r = mResources;
+ final Resources r = mResources.get();
final CharSequence title;
final CharSequence details;
- Icon icon = Icon.createWithResource(r, getIcon(transportType));
+ Icon icon = Icon.createWithResource(
+ mResources.getResourcesContext(), getIcon(transportType));
if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.wifi_no_internet, name);
details = r.getString(R.string.wifi_no_internet_detailed);
@@ -355,7 +356,7 @@
public void showToast(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) {
String fromTransport = getTransportName(approximateTransportType(fromNai));
String toTransport = getTransportName(approximateTransportType(toNai));
- String text = mResources.getString(
+ String text = mResources.get().getString(
R.string.network_switch_metered_toast, fromTransport, toTransport);
Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
}
diff --git a/service/src/com/android/server/connectivity/OsCompat.java b/service/src/com/android/server/connectivity/OsCompat.java
new file mode 100644
index 0000000..57e3dcd
--- /dev/null
+++ b/service/src/com/android/server/connectivity/OsCompat.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 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.server.connectivity;
+
+import android.system.ErrnoException;
+import android.system.Os;
+
+import java.io.FileDescriptor;
+
+/**
+ * Compatibility utility for android.system.Os core platform APIs.
+ *
+ * Connectivity has access to such APIs, but they are not part of the module_current stubs yet
+ * (only core_current). Most stable core platform APIs are included manually in the connectivity
+ * build rules, but because Os is also part of the base java SDK that is earlier on the
+ * classpath, the extra core platform APIs are not seen.
+ *
+ * TODO (b/157639992, b/183097033): remove as soon as core_current is part of system_server_current
+ * @hide
+ */
+public class OsCompat {
+ // This value should be correct on all architectures supported by Android, but hardcoding ioctl
+ // numbers should be avoided.
+ /**
+ * @see android.system.OsConstants#TIOCOUTQ
+ */
+ public static final int TIOCOUTQ = 0x5411;
+
+ /**
+ * @see android.system.Os#getsockoptInt(FileDescriptor, int, int)
+ */
+ public static int getsockoptInt(FileDescriptor fd, int level, int option) throws
+ ErrnoException {
+ try {
+ return (int) Os.class.getMethod(
+ "getsockoptInt", FileDescriptor.class, int.class, int.class)
+ .invoke(null, fd, level, option);
+ } catch (ReflectiveOperationException e) {
+ if (e.getCause() instanceof ErrnoException) {
+ throw (ErrnoException) e.getCause();
+ }
+ throw new IllegalStateException("Error calling getsockoptInt", e);
+ }
+ }
+
+ /**
+ * @see android.system.Os#ioctlInt(FileDescriptor, int)
+ */
+ public static int ioctlInt(FileDescriptor fd, int cmd) throws
+ ErrnoException {
+ try {
+ return (int) Os.class.getMethod(
+ "ioctlInt", FileDescriptor.class, int.class).invoke(null, fd, cmd);
+ } catch (ReflectiveOperationException e) {
+ if (e.getCause() instanceof ErrnoException) {
+ throw (ErrnoException) e.getCause();
+ }
+ throw new IllegalStateException("Error calling ioctlInt", e);
+ }
+ }
+}
diff --git a/service/src/com/android/server/connectivity/TcpKeepaliveController.java b/service/src/com/android/server/connectivity/TcpKeepaliveController.java
index c480594..73f3475 100644
--- a/service/src/com/android/server/connectivity/TcpKeepaliveController.java
+++ b/service/src/com/android/server/connectivity/TcpKeepaliveController.java
@@ -27,7 +27,8 @@
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IP_TOS;
import static android.system.OsConstants.IP_TTL;
-import static android.system.OsConstants.TIOCOUTQ;
+
+import static com.android.server.connectivity.OsCompat.TIOCOUTQ;
import android.annotation.NonNull;
import android.net.InvalidPacketException;
@@ -175,10 +176,10 @@
}
// Query write sequence number from SEND_QUEUE.
Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_SEND_QUEUE);
- tcpDetails.seq = Os.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
+ tcpDetails.seq = OsCompat.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
// Query read sequence number from RECV_QUEUE.
Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_RECV_QUEUE);
- tcpDetails.ack = Os.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
+ tcpDetails.ack = OsCompat.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
// Switch to NO_QUEUE to prevent illegal socket read/write in repair mode.
Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_NO_QUEUE);
// Finally, check if socket is still idle. TODO : this check needs to move to
@@ -198,9 +199,9 @@
tcpDetails.rcvWndScale = trw.rcvWndScale;
if (tcpDetails.srcAddress.length == 4 /* V4 address length */) {
// Query TOS.
- tcpDetails.tos = Os.getsockoptInt(fd, IPPROTO_IP, IP_TOS);
+ tcpDetails.tos = OsCompat.getsockoptInt(fd, IPPROTO_IP, IP_TOS);
// Query TTL.
- tcpDetails.ttl = Os.getsockoptInt(fd, IPPROTO_IP, IP_TTL);
+ tcpDetails.ttl = OsCompat.getsockoptInt(fd, IPPROTO_IP, IP_TTL);
}
} catch (ErrnoException e) {
Log.e(TAG, "Exception reading TCP state from socket", e);
@@ -305,7 +306,7 @@
private static boolean isReceiveQueueEmpty(FileDescriptor fd)
throws ErrnoException {
- final int result = Os.ioctlInt(fd, SIOCINQ);
+ final int result = OsCompat.ioctlInt(fd, SIOCINQ);
if (result != 0) {
Log.e(TAG, "Read queue has data");
return false;
@@ -315,7 +316,7 @@
private static boolean isSendQueueEmpty(FileDescriptor fd)
throws ErrnoException {
- final int result = Os.ioctlInt(fd, SIOCOUTQ);
+ final int result = OsCompat.ioctlInt(fd, SIOCOUTQ);
if (result != 0) {
Log.e(TAG, "Write queue has data");
return false;
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 8023aa0..33f704f 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -49,11 +49,16 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.TetheringManager.TetheringRequest;
import static android.net.cts.util.CtsNetUtils.ConnectivityActionReceiver;
import static android.net.cts.util.CtsNetUtils.HTTP_PORT;
import static android.net.cts.util.CtsNetUtils.NETWORK_CALLBACK_ACTION;
import static android.net.cts.util.CtsNetUtils.TEST_HOST;
import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
+import static android.net.cts.util.CtsTetheringUtils.StartTetheringCallback;
+import static android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback;
+import static android.net.cts.util.CtsTetheringUtils.isWifiTetheringSupported;
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
import static android.system.OsConstants.AF_INET;
@@ -90,6 +95,7 @@
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
+import android.net.ConnectivitySettingsManager;
import android.net.InetAddresses;
import android.net.IpSecManager;
import android.net.IpSecManager.UdpEncapsulationSocket;
@@ -106,6 +112,7 @@
import android.net.SocketKeepalive;
import android.net.TestNetworkInterface;
import android.net.TestNetworkManager;
+import android.net.TetheringManager;
import android.net.cts.util.CtsNetUtils;
import android.net.util.KeepaliveUtils;
import android.net.wifi.WifiManager;
@@ -220,6 +227,9 @@
private static final LinkAddress TEST_LINKADDR = new LinkAddress(
InetAddresses.parseNumericAddress("2001:db8::8"), 64);
+ private static final int AIRPLANE_MODE_OFF = 0;
+ private static final int AIRPLANE_MODE_ON = 1;
+
private Context mContext;
private Instrumentation mInstrumentation;
private ConnectivityManager mCm;
@@ -229,6 +239,7 @@
private final ArraySet<Integer> mNetworkTypes = new ArraySet<>();
private UiAutomation mUiAutomation;
private CtsNetUtils mCtsNetUtils;
+ private TetheringManager mTm;
// Used for cleanup purposes.
private final List<Range<Integer>> mVpnRequiredUidRanges = new ArrayList<>();
@@ -242,6 +253,7 @@
mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
mPackageManager = mContext.getPackageManager();
mCtsNetUtils = new CtsNetUtils(mContext);
+ mTm = mContext.getSystemService(TetheringManager.class);
if (DevSdkIgnoreRuleKt.isDevSdkInRange(null /* minExclusive */,
Build.VERSION_CODES.R /* maxInclusive */)) {
@@ -1922,4 +1934,76 @@
assertThrows(SecurityException.class, () -> mCm.setGlobalProxy(
ProxyInfo.buildDirectProxy("example.com" /* host */, 8080 /* port */)));
}
+
+ @Test
+ public void testFactoryResetWithoutPermission() {
+ assumeTrue(TestUtils.shouldTestSApis());
+ assertThrows(SecurityException.class, () -> mCm.factoryReset());
+ }
+
+ @Test
+ public void testFactoryReset() throws Exception {
+ assumeTrue(TestUtils.shouldTestSApis());
+
+ // Store current settings.
+ final int curAvoidBadWifi =
+ ConnectivitySettingsManager.getNetworkAvoidBadWifi(mContext);
+ final int curPrivateDnsMode = ConnectivitySettingsManager.getPrivateDnsMode(mContext);
+
+ final TestTetheringEventCallback tetherEventCallback = new TestTetheringEventCallback();
+ try {
+ mTm.registerTetheringEventCallback(c -> c.run() /* executor */, tetherEventCallback);
+ // Adopt for NETWORK_SETTINGS permission.
+ mUiAutomation.adoptShellPermissionIdentity();
+ // start tethering
+ tetherEventCallback.assumeWifiTetheringSupported(mContext);
+ startWifiTethering(tetherEventCallback);
+ // Update setting to verify the behavior.
+ mCm.setAirplaneMode(true);
+ ConnectivitySettingsManager.setPrivateDnsMode(mContext,
+ ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF);
+ ConnectivitySettingsManager.setNetworkAvoidBadWifi(mContext,
+ ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI_IGNORE);
+ assertEquals(AIRPLANE_MODE_ON, Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON));
+ // Verify factoryReset
+ mCm.factoryReset();
+ verifySettings(AIRPLANE_MODE_OFF,
+ ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC,
+ ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI_PROMPT);
+
+ tetherEventCallback.expectNoTetheringActive();
+ } finally {
+ // Restore settings.
+ mCm.setAirplaneMode(false);
+ ConnectivitySettingsManager.setNetworkAvoidBadWifi(mContext, curAvoidBadWifi);
+ ConnectivitySettingsManager.setPrivateDnsMode(mContext, curPrivateDnsMode);
+ mTm.unregisterTetheringEventCallback(tetherEventCallback);
+ mTm.stopAllTethering();
+ mUiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ private void verifySettings(int expectedAirplaneMode, int expectedPrivateDnsMode,
+ int expectedAvoidBadWifi) throws Exception {
+ assertEquals(expectedAirplaneMode, Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON));
+ assertEquals(expectedPrivateDnsMode,
+ ConnectivitySettingsManager.getPrivateDnsMode(mContext));
+ assertEquals(expectedAvoidBadWifi,
+ ConnectivitySettingsManager.getNetworkAvoidBadWifi(mContext));
+ }
+
+ private void startWifiTethering(final TestTetheringEventCallback callback) throws Exception {
+ if (!isWifiTetheringSupported(mContext, callback)) return;
+
+ final List<String> wifiRegexs =
+ callback.getTetheringInterfaceRegexps().getTetherableWifiRegexs();
+ final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
+ final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI)
+ .setShouldShowEntitlementUi(false).build();
+ mTm.startTethering(request, c -> c.run() /* executor */, startTetheringCallback);
+ startTetheringCallback.verifyTetheringStarted();
+ callback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI);
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/NetworkRequestTest.java b/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
index 8c35b97..63863da 100644
--- a/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
+++ b/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
@@ -30,6 +30,7 @@
import static junit.framework.Assert.fail;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@@ -418,6 +419,21 @@
}
}
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testGetCapabilities() {
+ final int[] netCapabilities = new int[] {
+ NET_CAPABILITY_INTERNET,
+ NET_CAPABILITY_NOT_ROAMING };
+ final NetworkCapabilities.Builder builder = NetworkCapabilities.Builder
+ .withoutDefaultCapabilities();
+ for (int capability : netCapabilities) builder.addCapability(capability);
+ final NetworkRequest nr = new NetworkRequest.Builder()
+ .clearCapabilities()
+ .setCapabilities(builder.build())
+ .build();
+ assertArrayEquals(netCapabilities, nr.getCapabilities());
+ }
+
@Test
public void testBuildRequestFromExistingRequestWithBuilder() {
assumeTrue(TestUtils.shouldTestSApis());
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
index c95dc28..c220326 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
@@ -22,7 +22,7 @@
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -35,6 +35,7 @@
import android.content.pm.PackageManager;
import android.net.Network;
import android.net.TetheredClient;
+import android.net.TetheringInterface;
import android.net.TetheringManager;
import android.net.TetheringManager.TetheringEventCallback;
import android.net.TetheringManager.TetheringInterfaceRegexps;
@@ -51,6 +52,7 @@
import java.util.Collection;
import java.util.List;
+import java.util.Set;
public final class CtsTetheringUtils {
private TetheringManager mTm;
@@ -116,23 +118,38 @@
}
}
- public static boolean isIfaceMatch(final List<String> ifaceRegexs, final List<String> ifaces) {
- return isIfaceMatch(ifaceRegexs.toArray(new String[0]), ifaces);
- }
-
- public static boolean isIfaceMatch(final String[] ifaceRegexs, final List<String> ifaces) {
+ private static boolean isRegexMatch(final String[] ifaceRegexs, String iface) {
if (ifaceRegexs == null) fail("ifaceRegexs should not be null");
+ for (String regex : ifaceRegexs) {
+ if (iface.matches(regex)) return true;
+ }
+
+ return false;
+ }
+
+ public static boolean isAnyIfaceMatch(final String[] ifaceRegexs, final List<String> ifaces) {
if (ifaces == null) return false;
for (String s : ifaces) {
- for (String regex : ifaceRegexs) {
- if (s.matches(regex)) {
- return true;
- }
+ if (isRegexMatch(ifaceRegexs, s)) return true;
+ }
+
+ return false;
+ }
+
+ private static TetheringInterface getFirstMatchingTetheringInterface(final List<String> regexs,
+ final int type, final Set<TetheringInterface> ifaces) {
+ if (ifaces == null || regexs == null) return null;
+
+ final String[] regexArray = regexs.toArray(new String[0]);
+ for (TetheringInterface iface : ifaces) {
+ if (isRegexMatch(regexArray, iface.getInterface()) && type == iface.getType()) {
+ return iface;
}
}
- return false;
+
+ return null;
}
// Must poll the callback before looking at the member.
@@ -171,6 +188,8 @@
private TetheringInterfaceRegexps mTetherableRegex;
private List<String> mTetherableIfaces;
private List<String> mTetheredIfaces;
+ private String mErrorIface;
+ private int mErrorCode;
@Override
public void onTetheringSupported(boolean supported) {
@@ -191,17 +210,41 @@
@Override
public void onTetherableInterfacesChanged(List<String> interfaces) {
mTetherableIfaces = interfaces;
+ }
+ // Call the interface default implementation, which will call
+ // onTetherableInterfacesChanged(List<String>). This ensures that the default implementation
+ // of the new callback method calls the old callback method and avoids the need to convert
+ // Set<TetheringInterface> to List<String> in this code.
+ @Override
+ public void onTetherableInterfacesChanged(Set<TetheringInterface> interfaces) {
+ TetheringEventCallback.super.onTetherableInterfacesChanged(interfaces);
+ assertHasAllTetheringInterfaces(interfaces, mTetherableIfaces);
mHistory.add(new CallbackValue(CallbackType.ON_TETHERABLE_IFACES, interfaces, 0));
}
@Override
public void onTetheredInterfacesChanged(List<String> interfaces) {
mTetheredIfaces = interfaces;
+ }
+
+ @Override
+ public void onTetheredInterfacesChanged(Set<TetheringInterface> interfaces) {
+ TetheringEventCallback.super.onTetheredInterfacesChanged(interfaces);
+ assertHasAllTetheringInterfaces(interfaces, mTetheredIfaces);
mHistory.add(new CallbackValue(CallbackType.ON_TETHERED_IFACES, interfaces, 0));
}
@Override
public void onError(String ifName, int error) {
+ mErrorIface = ifName;
+ mErrorCode = error;
+ }
+
+ @Override
+ public void onError(TetheringInterface ifName, int error) {
+ TetheringEventCallback.super.onError(ifName, error);
+ assertEquals(ifName.getInterface(), mErrorIface);
+ assertEquals(error, mErrorCode);
mHistory.add(new CallbackValue(CallbackType.ON_ERROR, ifName, error));
}
@@ -215,30 +258,66 @@
mHistory.add(new CallbackValue(CallbackType.ON_OFFLOAD_STATUS, status, 0));
}
- public void expectTetherableInterfacesChanged(@NonNull List<String> regexs) {
+ private void assertHasAllTetheringInterfaces(Set<TetheringInterface> tetheringIfaces,
+ List<String> ifaces) {
+ // This does not check that the interfaces are the same. This checks that the
+ // List<String> has all the interface names contained by the Set<TetheringInterface>.
+ assertEquals(tetheringIfaces.size(), ifaces.size());
+ for (TetheringInterface tether : tetheringIfaces) {
+ assertTrue("iface " + tether.getInterface()
+ + " seen by new callback but not old callback",
+ ifaces.contains(tether.getInterface()));
+ }
+ }
+
+ public void expectTetherableInterfacesChanged(@NonNull final List<String> regexs,
+ final int type) {
assertNotNull("No expected tetherable ifaces callback", mCurrent.poll(TIMEOUT_MS,
(cv) -> {
if (cv.callbackType != CallbackType.ON_TETHERABLE_IFACES) return false;
- final List<String> interfaces = (List<String>) cv.callbackParam;
- return isIfaceMatch(regexs, interfaces);
+ final Set<TetheringInterface> interfaces =
+ (Set<TetheringInterface>) cv.callbackParam;
+ return getFirstMatchingTetheringInterface(regexs, type, interfaces) != null;
}));
}
- public void expectTetheredInterfacesChanged(@NonNull List<String> regexs) {
- assertNotNull("No expected tethered ifaces callback", mCurrent.poll(TIMEOUT_MS,
- (cv) -> {
- if (cv.callbackType != CallbackType.ON_TETHERED_IFACES) return false;
+ public void expectNoTetheringActive() {
+ assertNotNull("At least one tethering type unexpectedly active",
+ mCurrent.poll(TIMEOUT_MS, (cv) -> {
+ if (cv.callbackType != CallbackType.ON_TETHERED_IFACES) return false;
- final List<String> interfaces = (List<String>) cv.callbackParam;
+ return ((Set<TetheringInterface>) cv.callbackParam).isEmpty();
+ }));
+ }
- // Null regexs means no active tethering.
- if (regexs == null) return interfaces.isEmpty();
+ public TetheringInterface expectTetheredInterfacesChanged(
+ @NonNull final List<String> regexs, final int type) {
+ while (true) {
+ final CallbackValue cv = mCurrent.poll(TIMEOUT_MS, c -> true);
+ if (cv == null) {
+ fail("No expected tethered ifaces callback, expected type: " + type);
+ }
- return isIfaceMatch(regexs, interfaces);
- }));
+ if (cv.callbackType != CallbackType.ON_TETHERED_IFACES) continue;
+
+ final Set<TetheringInterface> interfaces =
+ (Set<TetheringInterface>) cv.callbackParam;
+
+ final TetheringInterface iface =
+ getFirstMatchingTetheringInterface(regexs, type, interfaces);
+
+ if (iface != null) return iface;
+ }
}
public void expectCallbackStarted() {
+ // This method uses its own readhead because it just check whether last tethering status
+ // is updated after TetheringEventCallback get registered but do not check content
+ // of received callbacks. Using shared readhead (mCurrent) only when the callbacks the
+ // method polled is also not necessary for other methods which using shared readhead.
+ // All of methods using mCurrent is order mattered.
+ final ArrayTrackRecord<CallbackValue>.ReadHead history =
+ mHistory.newReadHead();
int receivedBitMap = 0;
// The each bit represent a type from CallbackType.ON_*.
// Expect all of callbacks except for ON_ERROR.
@@ -246,7 +325,7 @@
// Receive ON_ERROR on started callback is not matter. It just means tethering is
// failed last time, should able to continue the test this time.
while ((receivedBitMap & expectedBitMap) != expectedBitMap) {
- final CallbackValue cv = mCurrent.poll(TIMEOUT_MS, c -> true);
+ final CallbackValue cv = history.poll(TIMEOUT_MS, c -> true);
if (cv == null) {
fail("No expected callbacks, " + "expected bitmap: "
+ expectedBitMap + ", actual: " + receivedBitMap);
@@ -269,14 +348,14 @@
}));
}
- public void expectErrorOrTethered(final String iface) {
+ public void expectErrorOrTethered(final TetheringInterface iface) {
assertNotNull("No expected callback", mCurrent.poll(TIMEOUT_MS, (cv) -> {
if (cv.callbackType == CallbackType.ON_ERROR
- && iface.equals((String) cv.callbackParam)) {
+ && iface.equals((TetheringInterface) cv.callbackParam)) {
return true;
}
if (cv.callbackType == CallbackType.ON_TETHERED_IFACES
- && ((List<String>) cv.callbackParam).contains(iface)) {
+ && ((Set<TetheringInterface>) cv.callbackParam).contains(iface)) {
return true;
}
@@ -309,33 +388,12 @@
assumeTetheringSupported();
assumeTrue(!getTetheringInterfaceRegexps().getTetherableWifiRegexs().isEmpty());
-
- final PackageManager pm = ctx.getPackageManager();
- assumeTrue(pm.hasSystemFeature(PackageManager.FEATURE_WIFI));
-
- WifiManager wm = ctx.getSystemService(WifiManager.class);
- // Wifi feature flags only work when wifi is on.
- final boolean previousWifiEnabledState = wm.isWifiEnabled();
- try {
- if (!previousWifiEnabledState) SystemUtil.runShellCommand("svc wifi enable");
- waitForWifiEnabled(ctx);
- assumeTrue(wm.isPortableHotspotSupported());
- } finally {
- if (!previousWifiEnabledState) SystemUtil.runShellCommand("svc wifi disable");
- }
+ assumeTrue(isPortableHotspotSupported(ctx));
}
public TetheringInterfaceRegexps getTetheringInterfaceRegexps() {
return mTetherableRegex;
}
-
- public List<String> getTetherableInterfaces() {
- return mTetherableIfaces;
- }
-
- public List<String> getTetheredInterfaces() {
- return mTetheredIfaces;
- }
}
private static void waitForWifiEnabled(final Context ctx) throws Exception {
@@ -382,14 +440,31 @@
return callback.getTetheringInterfaceRegexps().getTetherableWifiRegexs();
}
- public static boolean isWifiTetheringSupported(final TestTetheringEventCallback callback) {
- return !getWifiTetherableInterfaceRegexps(callback).isEmpty();
+ public static boolean isWifiTetheringSupported(final Context ctx,
+ final TestTetheringEventCallback callback) throws Exception {
+ return !getWifiTetherableInterfaceRegexps(callback).isEmpty()
+ && isPortableHotspotSupported(ctx);
}
- public void startWifiTethering(final TestTetheringEventCallback callback)
+ /* Returns if wifi supports hotspot. */
+ private static boolean isPortableHotspotSupported(final Context ctx) throws Exception {
+ final PackageManager pm = ctx.getPackageManager();
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_WIFI)) return false;
+ final WifiManager wm = ctx.getSystemService(WifiManager.class);
+ // Wifi feature flags only work when wifi is on.
+ final boolean previousWifiEnabledState = wm.isWifiEnabled();
+ try {
+ if (!previousWifiEnabledState) SystemUtil.runShellCommand("svc wifi enable");
+ waitForWifiEnabled(ctx);
+ return wm.isPortableHotspotSupported();
+ } finally {
+ if (!previousWifiEnabledState) SystemUtil.runShellCommand("svc wifi disable");
+ }
+ }
+
+ public TetheringInterface startWifiTethering(final TestTetheringEventCallback callback)
throws InterruptedException {
final List<String> wifiRegexs = getWifiTetherableInterfaceRegexps(callback);
- assertFalse(isIfaceMatch(wifiRegexs, callback.getTetheredInterfaces()));
final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI)
@@ -397,11 +472,14 @@
mTm.startTethering(request, c -> c.run() /* executor */, startTetheringCallback);
startTetheringCallback.verifyTetheringStarted();
- callback.expectTetheredInterfacesChanged(wifiRegexs);
+ final TetheringInterface iface =
+ callback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI);
callback.expectOneOfOffloadStatusChanged(
TETHER_HARDWARE_OFFLOAD_STARTED,
TETHER_HARDWARE_OFFLOAD_FAILED);
+
+ return iface;
}
private static class StopSoftApCallback implements SoftApCallback {
@@ -441,7 +519,7 @@
public void stopWifiTethering(final TestTetheringEventCallback callback) {
mTm.stopTethering(TETHERING_WIFI);
expectSoftApDisabled();
- callback.expectTetheredInterfacesChanged(null);
+ callback.expectNoTetheringActive();
callback.expectOneOfOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
}
}
diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
index 71a81ff..0a5e506 100644
--- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -26,8 +26,7 @@
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_ERROR;
-import static android.net.cts.util.CtsTetheringUtils.isIfaceMatch;
-import static android.net.cts.util.CtsTetheringUtils.isWifiTetheringSupported;
+import static android.net.cts.util.CtsTetheringUtils.isAnyIfaceMatch;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -48,6 +47,7 @@
import android.net.LinkAddress;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.TetheringInterface;
import android.net.TetheringManager;
import android.net.TetheringManager.OnTetheringEntitlementResultListener;
import android.net.TetheringManager.TetheringInterfaceRegexps;
@@ -190,13 +190,13 @@
}
private boolean isIfaceActive(final String[] ifaceRegexs, final TetherState state) {
- return isIfaceMatch(ifaceRegexs, state.mActive);
+ return isAnyIfaceMatch(ifaceRegexs, state.mActive);
}
private void assertNoErroredIfaces(final TetherState state, final String[] ifaceRegexs) {
if (state == null || state.mErrored == null) return;
- if (isIfaceMatch(ifaceRegexs, state.mErrored)) {
+ if (isAnyIfaceMatch(ifaceRegexs, state.mErrored)) {
fail("Found failed tethering interfaces: " + Arrays.toString(state.mErrored.toArray()));
}
}
@@ -256,12 +256,13 @@
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
+ tetherEventCallback.expectNoTetheringActive();
- mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
+ final TetheringInterface tetheredIface =
+ mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
- final List<String> tetheredIfaces = tetherEventCallback.getTetheredInterfaces();
- assertEquals(1, tetheredIfaces.size());
- final String wifiTetheringIface = tetheredIfaces.get(0);
+ assertNotNull(tetheredIface);
+ final String wifiTetheringIface = tetheredIface.getInterface();
mCtsTetheringUtils.stopWifiTethering(tetherEventCallback);
@@ -272,7 +273,8 @@
if (ret == TETHER_ERROR_NO_ERROR) {
// If calling #tether successful, there is a callback to tell the result of
// tethering setup.
- tetherEventCallback.expectErrorOrTethered(wifiTetheringIface);
+ tetherEventCallback.expectErrorOrTethered(
+ new TetheringInterface(TETHERING_WIFI, wifiTetheringIface));
}
} finally {
mTM.untether(wifiTetheringIface);
@@ -319,7 +321,7 @@
mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
mTM.stopAllTethering();
- tetherEventCallback.expectTetheredInterfacesChanged(null);
+ tetherEventCallback.expectNoTetheringActive();
} finally {
mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
}
@@ -417,6 +419,7 @@
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
+ tetherEventCallback.expectNoTetheringActive();
previousWifiEnabledState = mWm.isWifiEnabled();
if (previousWifiEnabledState) {
diff --git a/tests/integration/src/android/net/TestNetworkStackClient.kt b/tests/integration/src/android/net/TestNetworkStackClient.kt
index 01eb514..61ef5bd 100644
--- a/tests/integration/src/android/net/TestNetworkStackClient.kt
+++ b/tests/integration/src/android/net/TestNetworkStackClient.kt
@@ -19,6 +19,7 @@
import android.content.ComponentName
import android.content.Context
import android.content.Intent
+import android.net.networkstack.NetworkStackClientBase
import android.os.IBinder
import com.android.server.net.integrationtests.TestNetworkStackService
import org.mockito.Mockito.any
@@ -29,28 +30,22 @@
const val TEST_ACTION_SUFFIX = ".Test"
-class TestNetworkStackClient(context: Context) : NetworkStackClient(TestDependencies(context)) {
+class TestNetworkStackClient(private val context: Context) : NetworkStackClientBase() {
// TODO: consider switching to TrackRecord for more expressive checks
private val lastCallbacks = HashMap<Network, INetworkMonitorCallbacks>()
+ private val moduleConnector = ConnectivityModuleConnector { _, action, _, _ ->
+ val intent = Intent(action)
+ val serviceName = TestNetworkStackService::class.qualifiedName
+ ?: fail("TestNetworkStackService name not found")
+ intent.component = ComponentName(context.packageName, serviceName)
+ return@ConnectivityModuleConnector intent
+ }.also { it.init(context) }
- private class TestDependencies(private val context: Context) : Dependencies {
- override fun addToServiceManager(service: IBinder) = Unit
- override fun checkCallerUid() = Unit
-
- override fun getConnectivityModuleConnector(): ConnectivityModuleConnector {
- return ConnectivityModuleConnector { _, _, _, inSystemProcess ->
- getNetworkStackIntent(inSystemProcess)
- }.also { it.init(context) }
- }
-
- private fun getNetworkStackIntent(inSystemProcess: Boolean): Intent? {
- // Simulate out-of-system-process config: in-process service not found (null intent)
- if (inSystemProcess) return null
- val intent = Intent(INetworkStackConnector::class.qualifiedName + TEST_ACTION_SUFFIX)
- val serviceName = TestNetworkStackService::class.qualifiedName
- ?: fail("TestNetworkStackService name not found")
- intent.component = ComponentName(context.packageName, serviceName)
- return intent
+ fun start() {
+ moduleConnector.startModuleService(
+ INetworkStackConnector::class.qualifiedName + TEST_ACTION_SUFFIX,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) { connector ->
+ onNetworkStackConnected(INetworkStackConnector.Stub.asInterface(connector))
}
}
diff --git a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index b6e4274..e039ef0 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -157,7 +157,6 @@
doReturn(IntArray(0)).`when`(systemConfigManager).getSystemPermissionUids(anyString())
networkStackClient = TestNetworkStackClient(realContext)
- networkStackClient.init()
networkStackClient.start()
service = TestConnectivityService(makeDependencies())
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 1b4f836..04e63fc 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -228,7 +228,6 @@
import android.net.NetworkScore;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
-import android.net.NetworkStackClient;
import android.net.NetworkStateSnapshot;
import android.net.NetworkTestResultParcelable;
import android.net.OemNetworkPreferences;
@@ -248,6 +247,7 @@
import android.net.VpnManager;
import android.net.VpnTransportInfo;
import android.net.metrics.IpConnectivityLog;
+import android.net.networkstack.NetworkStackClientBase;
import android.net.resolv.aidl.Nat64PrefixEventParcel;
import android.net.resolv.aidl.PrivateDnsValidationEventParcel;
import android.net.shared.NetworkMonitorUtils;
@@ -461,7 +461,7 @@
@Mock NetworkStatsManager mStatsManager;
@Mock IDnsResolver mMockDnsResolver;
@Mock INetd mMockNetd;
- @Mock NetworkStackClient mNetworkStack;
+ @Mock NetworkStackClientBase mNetworkStack;
@Mock PackageManager mPackageManager;
@Mock UserManager mUserManager;
@Mock NotificationManager mNotificationManager;