Merge "Revert "Add min_sdk_version to connectivity targets""
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/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/api/module-lib-current.txt b/framework/api/module-lib-current.txt
index 6c454bc..7fc0382 100644
--- a/framework/api/module-lib-current.txt
+++ b/framework/api/module-lib-current.txt
@@ -48,7 +48,6 @@
public class ConnectivitySettingsManager {
method public static void clearGlobalProxy(@NonNull android.content.Context);
- method @NonNull public static java.util.Set<java.lang.String> getAppsAllowedOnRestrictedNetworks(@NonNull android.content.Context);
method @Nullable public static String getCaptivePortalHttpUrl(@NonNull android.content.Context);
method public static int getCaptivePortalMode(@NonNull android.content.Context, int);
method @NonNull public static java.time.Duration getConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
@@ -66,9 +65,9 @@
method @NonNull public static String getPrivateDnsDefaultMode(@NonNull android.content.Context);
method @Nullable public static String getPrivateDnsHostname(@NonNull android.content.Context);
method public static int getPrivateDnsMode(@NonNull android.content.Context);
+ method @NonNull public static java.util.Set<java.lang.Integer> getUidsAllowedOnRestrictedNetworks(@NonNull android.content.Context);
method public static boolean getWifiAlwaysRequested(@NonNull android.content.Context, boolean);
method @NonNull public static java.time.Duration getWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
- method public static void setAppsAllowedOnRestrictedNetworks(@NonNull android.content.Context, @NonNull java.util.Set<java.lang.String>);
method public static void setCaptivePortalHttpUrl(@NonNull android.content.Context, @Nullable String);
method public static void setCaptivePortalMode(@NonNull android.content.Context, int);
method public static void setConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
@@ -86,6 +85,7 @@
method public static void setPrivateDnsDefaultMode(@NonNull android.content.Context, @NonNull int);
method public static void setPrivateDnsHostname(@NonNull android.content.Context, @Nullable String);
method public static void setPrivateDnsMode(@NonNull android.content.Context, int);
+ method public static void setUidsAllowedOnRestrictedNetworks(@NonNull android.content.Context, @NonNull java.util.Set<java.lang.Integer>);
method public static void setWifiAlwaysRequested(@NonNull android.content.Context, boolean);
method public static void setWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
field public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; // 0x2
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 1a6b37b..a64a9e6 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -3341,9 +3341,10 @@
* Register or update a network offer with ConnectivityService.
*
* ConnectivityService keeps track of offers made by the various providers and matches
- * them to networking requests made by apps or the system. The provider supplies a score
- * and the capabilities of the network it might be able to bring up ; these act as filters
- * used by ConnectivityService to only send those requests that can be fulfilled by the
+ * them to networking requests made by apps or the system. A callback identifies an offer
+ * uniquely, and later calls with the same callback update the offer. The provider supplies a
+ * score and the capabilities of the network it might be able to bring up ; these act as
+ * filters used by ConnectivityService to only send those requests that can be fulfilled by the
* provider.
*
* The provider is under no obligation to be able to bring up the network it offers at any
@@ -3364,11 +3365,11 @@
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
android.Manifest.permission.NETWORK_FACTORY})
- public void offerNetwork(@NonNull final NetworkProvider provider,
+ public void offerNetwork(@NonNull final int providerId,
@NonNull final NetworkScore score, @NonNull final NetworkCapabilities caps,
@NonNull final INetworkOfferCallback callback) {
try {
- mService.offerNetwork(Objects.requireNonNull(provider.getMessenger(), "null messenger"),
+ mService.offerNetwork(providerId,
Objects.requireNonNull(score, "null score"),
Objects.requireNonNull(caps, "null caps"),
Objects.requireNonNull(callback, "null callback"));
@@ -4916,7 +4917,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/ConnectivitySettingsManager.java b/framework/src/android/net/ConnectivitySettingsManager.java
index 762f24f..5f7f539 100644
--- a/framework/src/android/net/ConnectivitySettingsManager.java
+++ b/framework/src/android/net/ConnectivitySettingsManager.java
@@ -374,12 +374,12 @@
private static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME_STRING = "hostname";
/**
- * A list of apps that is allowed on restricted networks.
+ * A list of uids that is allowed to use restricted networks.
*
* @hide
*/
- public static final String APPS_ALLOWED_ON_RESTRICTED_NETWORKS =
- "apps_allowed_on_restricted_networks";
+ public static final String UIDS_ALLOWED_ON_RESTRICTED_NETWORKS =
+ "uids_allowed_on_restricted_networks";
/**
* Get mobile data activity timeout from {@link Settings}.
@@ -1003,6 +1003,28 @@
context.getContentResolver(), NETWORK_METERED_MULTIPATH_PREFERENCE, preference);
}
+ private static Set<Integer> getUidSetFromString(@Nullable String uidList) {
+ final Set<Integer> uids = new ArraySet<>();
+ if (TextUtils.isEmpty(uidList)) {
+ return uids;
+ }
+ for (String uid : uidList.split(";")) {
+ uids.add(Integer.valueOf(uid));
+ }
+ return uids;
+ }
+
+ private static String getUidStringFromSet(@NonNull Set<Integer> uidList) {
+ final StringJoiner joiner = new StringJoiner(";");
+ for (Integer uid : uidList) {
+ if (uid < 0 || UserHandle.getAppId(uid) > Process.LAST_APPLICATION_UID) {
+ throw new IllegalArgumentException("Invalid uid");
+ }
+ joiner.add(uid.toString());
+ }
+ return joiner.toString();
+ }
+
/**
* Get the list of uids(from {@link Settings}) that should go on cellular networks in preference
* even when higher-priority networks are connected.
@@ -1015,14 +1037,7 @@
public static Set<Integer> getMobileDataPreferredUids(@NonNull Context context) {
final String uidList = Settings.Secure.getString(
context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS);
- final Set<Integer> uids = new ArraySet<>();
- if (TextUtils.isEmpty(uidList)) {
- return uids;
- }
- for (String uid : uidList.split(";")) {
- uids.add(Integer.valueOf(uid));
- }
- return uids;
+ return getUidSetFromString(uidList);
}
/**
@@ -1035,53 +1050,41 @@
*/
public static void setMobileDataPreferredUids(@NonNull Context context,
@NonNull Set<Integer> uidList) {
- final StringJoiner joiner = new StringJoiner(";");
- for (Integer uid : uidList) {
- if (uid < 0 || UserHandle.getAppId(uid) > Process.LAST_APPLICATION_UID) {
- throw new IllegalArgumentException("Invalid uid");
- }
- joiner.add(uid.toString());
- }
- Settings.Secure.putString(
- context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS, joiner.toString());
+ final String uids = getUidStringFromSet(uidList);
+ Settings.Secure.putString(context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS, uids);
}
/**
- * Get the list of apps(from {@link Settings}) that is allowed on restricted networks.
+ * Get the list of uids (from {@link Settings}) allowed to use restricted networks.
+ *
+ * Access to restricted networks is controlled by the (preinstalled-only)
+ * CONNECTIVITY_USE_RESTRICTED_NETWORKS permission, but highly privileged
+ * callers can also set a list of uids that can access restricted networks.
+ *
+ * This is useful for example in some jurisdictions where government apps,
+ * that can't be preinstalled, must still have access to emergency services.
*
* @param context The {@link Context} to query the setting.
- * @return A list of apps that is allowed on restricted networks or null if no setting
+ * @return A list of uids that is allowed to use restricted networks or null if no setting
* value.
*/
@NonNull
- public static Set<String> getAppsAllowedOnRestrictedNetworks(@NonNull Context context) {
- final String appList = Settings.Secure.getString(
- context.getContentResolver(), APPS_ALLOWED_ON_RESTRICTED_NETWORKS);
- if (TextUtils.isEmpty(appList)) {
- return new ArraySet<>();
- }
- return new ArraySet<>(appList.split(";"));
+ public static Set<Integer> getUidsAllowedOnRestrictedNetworks(@NonNull Context context) {
+ final String uidList = Settings.Secure.getString(
+ context.getContentResolver(), UIDS_ALLOWED_ON_RESTRICTED_NETWORKS);
+ return getUidSetFromString(uidList);
}
/**
- * Set the list of apps(from {@link Settings}) that is allowed on restricted networks.
- *
- * Note: Please refer to android developer guidelines for valid app(package name).
- * https://developer.android.com/guide/topics/manifest/manifest-element.html#package
+ * Set the list of uids(from {@link Settings}) that is allowed to use restricted networks.
*
* @param context The {@link Context} to set the setting.
- * @param list A list of apps that is allowed on restricted networks.
+ * @param uidList A list of uids that is allowed to use restricted networks.
*/
- public static void setAppsAllowedOnRestrictedNetworks(@NonNull Context context,
- @NonNull Set<String> list) {
- final StringJoiner joiner = new StringJoiner(";");
- for (String app : list) {
- if (app == null || app.contains(";")) {
- throw new IllegalArgumentException("Invalid app(package name)");
- }
- joiner.add(app);
- }
- Settings.Secure.putString(context.getContentResolver(), APPS_ALLOWED_ON_RESTRICTED_NETWORKS,
- joiner.toString());
+ public static void setUidsAllowedOnRestrictedNetworks(@NonNull Context context,
+ @NonNull Set<Integer> uidList) {
+ final String uids = getUidStringFromSet(uidList);
+ Settings.Secure.putString(context.getContentResolver(), UIDS_ALLOWED_ON_RESTRICTED_NETWORKS,
+ uids);
}
}
diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl
index d937c9c..c434bbc 100644
--- a/framework/src/android/net/IConnectivityManager.aidl
+++ b/framework/src/android/net/IConnectivityManager.aidl
@@ -223,7 +223,7 @@
int getRestrictBackgroundStatusByCaller();
- void offerNetwork(in Messenger messenger, in NetworkScore score,
+ void offerNetwork(int providerId, in NetworkScore score,
in NetworkCapabilities caps, in INetworkOfferCallback callback);
void unofferNetwork(in INetworkOfferCallback callback);
}
diff --git a/framework/src/android/net/INetworkOfferCallback.aidl b/framework/src/android/net/INetworkOfferCallback.aidl
index 67d2d40..a6de173 100644
--- a/framework/src/android/net/INetworkOfferCallback.aidl
+++ b/framework/src/android/net/INetworkOfferCallback.aidl
@@ -22,15 +22,16 @@
* A callback registered with connectivity by network providers together with
* a NetworkOffer.
*
- * When the offer is needed to satisfy some application or system component,
- * connectivity will call onOfferNeeded on this callback. When this happens,
- * the provider should try and bring up the network.
+ * When the network for this offer is needed to satisfy some application or
+ * system component, connectivity will call onNetworkNeeded on this callback.
+ * When this happens, the provider should try and bring up the network.
*
- * When the offer is no longer needed, for example because the application has
- * withdrawn the request or if the request is being satisfied by a network
- * that this offer will never be able to beat, connectivity calls
- * onOfferUnneeded. When this happens, the provider should stop trying to
- * bring up the network, or tear it down if it has already been brought up.
+ * When the network for this offer is no longer needed, for example because
+ * the application has withdrawn the request or if the request is being
+ * satisfied by a network that this offer will never be able to beat,
+ * connectivity calls onNetworkUnneeded. When this happens, the provider
+ * should stop trying to bring up the network, or tear it down if it has
+ * already been brought up.
*
* When NetworkProvider#offerNetwork is called, the provider can expect to
* immediately receive all requests that can be fulfilled by that offer and
@@ -38,25 +39,25 @@
* request is currently outstanding, because no requests have been made that
* can be satisfied by this offer, or because all such requests are already
* satisfied by a better network.
- * onOfferNeeded can be called at any time after registration and until the
+ * onNetworkNeeded can be called at any time after registration and until the
* offer is withdrawn with NetworkProvider#unofferNetwork is called. This
* typically happens when a new network request is filed by an application,
* or when the network satisfying a request disconnects and this offer now
- * stands a chance to be the best network for it.
+ * stands a chance to supply the best network for it.
*
* @hide
*/
oneway interface INetworkOfferCallback {
/**
- * Informs the registrant that the offer is needed to fulfill this request.
+ * Called when a network for this offer is needed to fulfill this request.
* @param networkRequest the request to satisfy
* @param providerId the ID of the provider currently satisfying
* this request, or NetworkProvider.ID_NONE if none.
*/
- void onOfferNeeded(in NetworkRequest networkRequest, int providerId);
+ void onNetworkNeeded(in NetworkRequest networkRequest, int providerId);
/**
- * Informs the registrant that the offer is no longer needed to fulfill this request.
+ * Informs the registrant that the offer is no longer valuable to fulfill this request.
*/
- void onOfferUnneeded(in NetworkRequest networkRequest);
+ void onNetworkUnneeded(in NetworkRequest networkRequest);
}
diff --git a/framework/src/android/net/NetworkProvider.java b/framework/src/android/net/NetworkProvider.java
index 8f93047..d859022 100644
--- a/framework/src/android/net/NetworkProvider.java
+++ b/framework/src/android/net/NetworkProvider.java
@@ -170,10 +170,11 @@
/** @hide */
// TODO : make @SystemApi when the impl is complete
public interface NetworkOfferCallback {
- /** Called by the system when this offer is needed to satisfy some networking request. */
- void onOfferNeeded(@NonNull NetworkRequest request, int providerId);
- /** Called by the system when this offer is no longer needed. */
- void onOfferUnneeded(@NonNull NetworkRequest request);
+ /** Called by the system when a network for this offer is needed to satisfy some
+ * networking request. */
+ void onNetworkNeeded(@NonNull NetworkRequest request, int providerId);
+ /** Called by the system when this offer is no longer valuable for this request. */
+ void onNetworkUnneeded(@NonNull NetworkRequest request);
}
private class NetworkOfferCallbackProxy extends INetworkOfferCallback.Stub {
@@ -187,14 +188,14 @@
}
@Override
- public void onOfferNeeded(final @NonNull NetworkRequest request,
+ public void onNetworkNeeded(final @NonNull NetworkRequest request,
final int providerId) {
- mExecutor.execute(() -> callback.onOfferNeeded(request, providerId));
+ mExecutor.execute(() -> callback.onNetworkNeeded(request, providerId));
}
@Override
- public void onOfferUnneeded(final @NonNull NetworkRequest request) {
- mExecutor.execute(() -> callback.onOfferUnneeded(request));
+ public void onNetworkUnneeded(final @NonNull NetworkRequest request) {
+ mExecutor.execute(() -> callback.onNetworkUnneeded(request));
}
}
@@ -213,41 +214,41 @@
}
/**
- * Register or update an offer for network with the passed caps and score.
+ * Register or update an offer for network with the passed capabilities and score.
*
- * A NetworkProvider's job is to provide networks. This function is how a provider tells the
+ * A NetworkProvider's role is to provide networks. This method is how a provider tells the
* connectivity stack what kind of network it may provide. The score and caps arguments act
- * as filters that the connectivity stack uses to tell when the offer is necessary. When an
- * offer might be advantageous over existing networks, the provider will receive a call to
- * the associated callback's {@link NetworkOfferCallback#onOfferNeeded} method. The provider
- * should then try to bring up this network. When an offer is no longer needed, the stack
- * will inform the provider by calling {@link NetworkOfferCallback#onOfferUnneeded}. The
+ * as filters that the connectivity stack uses to tell when the offer is valuable. When an
+ * offer might be preferred over existing networks, the provider will receive a call to
+ * the associated callback's {@link NetworkOfferCallback#onNetworkNeeded} method. The provider
+ * should then try to bring up this network. When an offer is no longer useful, the stack
+ * will inform the provider by calling {@link NetworkOfferCallback#onNetworkUnneeded}. The
* provider should stop trying to bring up such a network, or disconnect it if it already has
* one.
*
- * The stack determines what offers are needed according to what networks are currently
+ * The stack determines what offers are valuable according to what networks are currently
* available to the system, and what networking requests are made by applications. If an
- * offer looks like it could be a better choice than any existing network for any particular
- * request, that's when the stack decides the offer is needed. If the current networking
- * requests are all satisfied by networks that this offer can't possibly be a better match
- * for, that's when the offer is unneeded. An offer starts off as unneeded ; the provider
- * should not try to bring up the network until {@link NetworkOfferCallback#onOfferNeeded}
- * is called.
+ * offer looks like it could connect a better network than any existing network for any
+ * particular request, that's when the stack decides the network is needed. If the current
+ * networking requests are all satisfied by networks that this offer couldn't possibly be a
+ * better match for, that's when the offer is no longer valuable. An offer starts out as
+ * unneeded ; the provider should not try to bring up the network until
+ * {@link NetworkOfferCallback#onNetworkNeeded} is called.
*
* Note that the offers are non-binding to the providers, in particular because providers
* often don't know if they will be able to bring up such a network at any given time. For
- * example, no wireless network may be in range when the offer is needed. This is fine and
- * expected ; the provider should simply continue to try to bring up the network and do so
+ * example, no wireless network may be in range when the offer would be valuable. This is fine
+ * and expected ; the provider should simply continue to try to bring up the network and do so
* if/when it becomes possible. In the mean time, the stack will continue to satisfy requests
* with the best network currently available, or if none, keep the apps informed that no
* network can currently satisfy this request. When/if the provider can bring up the network,
* the connectivity stack will match it against requests, and inform interested apps of the
* availability of this network. This may, in turn, render the offer of some other provider
- * unneeded if all requests it used to satisfy are now better served by this network.
+ * low-value if all requests it used to satisfy are now better served by this network.
*
* A network can become unneeded for a reason like the above : whether the provider managed
* to bring up the offered network after it became needed or not, some other provider may
- * bring up a better network than this one, making this offer unneeded. A network may also
+ * bring up a better network than this one, making this network unneeded. A network may also
* become unneeded if the application making the request withdrew it (for example, after it
* is done transferring data, or if the user canceled an operation).
*
@@ -272,6 +273,9 @@
public void offerNetwork(@NonNull final NetworkScore score,
@NonNull final NetworkCapabilities caps, @NonNull final Executor executor,
@NonNull final NetworkOfferCallback callback) {
+ // Can't offer a network with a provider that is not yet registered or already unregistered.
+ final int providerId = mProviderId;
+ if (providerId == ID_NONE) return;
NetworkOfferCallbackProxy proxy = null;
synchronized (mProxies) {
for (final NetworkOfferCallbackProxy existingProxy : mProxies) {
@@ -285,7 +289,8 @@
mProxies.add(proxy);
}
}
- mContext.getSystemService(ConnectivityManager.class).offerNetwork(this, score, caps, proxy);
+ mContext.getSystemService(ConnectivityManager.class)
+ .offerNetwork(providerId, score, caps, proxy);
}
/**
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index adfcace..3771bbc 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -617,6 +617,11 @@
private static final int EVENT_UNREGISTER_NETWORK_OFFER = 53;
/**
+ * Used internally when MOBILE_DATA_PREFERRED_UIDS setting changed.
+ */
+ private static final int EVENT_MOBILE_DATA_PREFERRED_UIDS_CHANGED = 54;
+
+ /**
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
* should be shown.
*/
@@ -1460,6 +1465,11 @@
mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
}
+ @VisibleForTesting
+ void updateMobileDataPreferredUids() {
+ mHandler.sendEmptyMessage(EVENT_MOBILE_DATA_PREFERRED_UIDS_CHANGED);
+ }
+
private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, int id) {
final boolean enable = mContext.getResources().getBoolean(id);
handleAlwaysOnNetworkRequest(networkRequest, enable);
@@ -1504,6 +1514,8 @@
vehicleAlwaysRequested || legacyAlwaysRequested);
}
+ // Note that registering observer for setting do not get initial callback when registering,
+ // callers might have self-initialization to update status if need.
private void registerSettingsCallbacks() {
// Watch for global HTTP proxy changes.
mSettingsObserver.observe(
@@ -1519,6 +1531,11 @@
mSettingsObserver.observe(
Settings.Global.getUriFor(ConnectivitySettingsManager.WIFI_ALWAYS_REQUESTED),
EVENT_CONFIGURE_ALWAYS_ON_NETWORKS);
+
+ // Watch for mobile data preferred uids changes.
+ mSettingsObserver.observe(
+ Settings.Secure.getUriFor(ConnectivitySettingsManager.MOBILE_DATA_PREFERRED_UIDS),
+ EVENT_MOBILE_DATA_PREFERRED_UIDS_CHANGED);
}
private void registerPrivateDnsSettingsCallbacks() {
@@ -2717,6 +2734,13 @@
// Create network requests for always-on networks.
mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS));
+
+ // Update mobile data preference if necessary.
+ // Note that empty uid list can be skip here only because no uid rules applied before system
+ // ready. Normally, the empty uid list means to clear the uids rules on netd.
+ if (!ConnectivitySettingsManager.getMobileDataPreferredUids(mContext).isEmpty()) {
+ updateMobileDataPreferredUids();
+ }
}
/**
@@ -4801,6 +4825,9 @@
case EVENT_REPORT_NETWORK_ACTIVITY:
mNetworkActivityTracker.handleReportNetworkActivity();
break;
+ case EVENT_MOBILE_DATA_PREFERRED_UIDS_CHANGED:
+ handleMobileDataPreferredUidsChanged();
+ break;
}
}
}
@@ -5605,7 +5632,7 @@
* Get the list of UIDs this nri applies to.
*/
@NonNull
- private Set<UidRange> getUids() {
+ Set<UidRange> getUids() {
// networkCapabilities.getUids() returns a defensive copy.
// multilayer requests will all have the same uids so return the first one.
final Set<UidRange> uids = mRequests.get(0).networkCapabilities.getUidRanges();
@@ -6233,11 +6260,11 @@
}
@Override
- public void offerNetwork(@NonNull final Messenger providerMessenger,
+ public void offerNetwork(final int providerId,
@NonNull final NetworkScore score, @NonNull final NetworkCapabilities caps,
@NonNull final INetworkOfferCallback callback) {
final NetworkOffer offer = new NetworkOffer(
- FullScore.makeProspectiveScore(score, caps), caps, callback, providerMessenger);
+ FullScore.makeProspectiveScore(score, caps), caps, callback, providerId);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_OFFER, offer));
}
@@ -6255,7 +6282,7 @@
// Unregister all the offers from this provider
final ArrayList<NetworkOfferInfo> toRemove = new ArrayList<>();
for (final NetworkOfferInfo noi : mNetworkOffers) {
- if (noi.offer.provider == messenger) {
+ if (noi.offer.providerId == npi.providerId) {
// Can't call handleUnregisterNetworkOffer here because iteration is in progress
toRemove.add(noi);
}
@@ -6319,6 +6346,11 @@
@NonNull
private ProfileNetworkPreferences mProfileNetworkPreferences = new ProfileNetworkPreferences();
+ // A set of UIDs that should use mobile data preferentially if available. This object follows
+ // the same threading rules as the OEM network preferences above.
+ @NonNull
+ private Set<Integer> mMobileDataPreferredUids = new ArraySet<>();
+
// OemNetworkPreferences activity String log entries.
private static final int MAX_OEM_NETWORK_PREFERENCE_LOGS = 20;
@NonNull
@@ -6640,6 +6672,13 @@
}
}
+ private boolean isNetworkProviderWithIdRegistered(final int providerId) {
+ for (final NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
+ if (npi.providerId == providerId) return true;
+ }
+ return false;
+ }
+
/**
* Register or update a network offer.
* @param newOffer The new offer. If the callback member is the same as an existing
@@ -6647,7 +6686,7 @@
*/
private void handleRegisterNetworkOffer(@NonNull final NetworkOffer newOffer) {
ensureRunningOnConnectivityServiceThread();
- if (null == mNetworkProviderInfos.get(newOffer.provider)) {
+ if (!isNetworkProviderWithIdRegistered(newOffer.providerId)) {
// This may actually happen if a provider updates its score or registers and then
// immediately unregisters. The offer would still be in the handler queue, but the
// provider would have been removed.
@@ -6662,7 +6701,7 @@
}
final NetworkOfferInfo noi = new NetworkOfferInfo(newOffer);
try {
- noi.offer.provider.getBinder().linkToDeath(noi, 0 /* flags */);
+ noi.offer.callback.asBinder().linkToDeath(noi, 0 /* flags */);
} catch (RemoteException e) {
noi.binderDied();
return;
@@ -6674,7 +6713,7 @@
private void handleUnregisterNetworkOffer(@NonNull final NetworkOfferInfo noi) {
ensureRunningOnConnectivityServiceThread();
mNetworkOffers.remove(noi);
- noi.offer.provider.getBinder().unlinkToDeath(noi, 0 /* flags */);
+ noi.offer.callback.asBinder().unlinkToDeath(noi, 0 /* flags */);
}
@Nullable private NetworkOfferInfo findNetworkOfferInfoByCallback(
@@ -9679,7 +9718,8 @@
// safe - it's just possible the value is slightly outdated. For the final check,
// see #handleSetProfileNetworkPreference. But if this can be caught here it is a
// lot easier to understand, so opportunistically check it.
- if (!mOemNetworkPreferences.isEmpty()) {
+ // TODO: Have a priority for each preference.
+ if (!mOemNetworkPreferences.isEmpty() || !mMobileDataPreferredUids.isEmpty()) {
throwConcurrentPreferenceException();
}
final NetworkCapabilities nc;
@@ -9738,7 +9778,8 @@
// The binder call has already checked this, but as mOemNetworkPreferences is only
// touched on the handler thread, it's theoretically not impossible that it has changed
// since.
- if (!mOemNetworkPreferences.isEmpty()) {
+ // TODO: Have a priority for each preference.
+ if (!mOemNetworkPreferences.isEmpty() || !mMobileDataPreferredUids.isEmpty()) {
// This may happen on a device with an OEM preference set when a user is removed.
// In this case, it's safe to ignore. In particular this happens in the tests.
loge("handleSetProfileNetworkPreference, but OEM network preferences not empty");
@@ -9767,6 +9808,56 @@
}
}
+ @VisibleForTesting
+ @NonNull
+ ArraySet<NetworkRequestInfo> createNrisFromMobileDataPreferredUids(
+ @NonNull final Set<Integer> uids) {
+ final ArraySet<NetworkRequestInfo> nris = new ArraySet<>();
+ if (uids.size() == 0) {
+ // Should not create NetworkRequestInfo if no preferences. Without uid range in
+ // NetworkRequestInfo, makeDefaultForApps() would treat it as a illegal NRI.
+ if (DBG) log("Don't create NetworkRequestInfo because no preferences");
+ return nris;
+ }
+
+ final List<NetworkRequest> requests = new ArrayList<>();
+ // The NRI should be comprised of two layers:
+ // - The request for the mobile network preferred.
+ // - The request for the default network, for fallback.
+ requests.add(createDefaultInternetRequestForTransport(
+ TRANSPORT_CELLULAR, NetworkRequest.Type.LISTEN));
+ requests.add(createDefaultInternetRequestForTransport(
+ TYPE_NONE, NetworkRequest.Type.TRACK_DEFAULT));
+ final Set<UidRange> ranges = new ArraySet<>();
+ for (final int uid : uids) {
+ ranges.add(new UidRange(uid, uid));
+ }
+ setNetworkRequestUids(requests, ranges);
+ nris.add(new NetworkRequestInfo(Process.myUid(), requests));
+ return nris;
+ }
+
+ private void handleMobileDataPreferredUidsChanged() {
+ // Ignore update preference because it's not clear what preference should win in case both
+ // apply to the same app.
+ // TODO: Have a priority for each preference.
+ if (!mOemNetworkPreferences.isEmpty() || !mProfileNetworkPreferences.isEmpty()) {
+ loge("Ignore mobile data preference change because other preferences are not empty");
+ return;
+ }
+
+ mMobileDataPreferredUids = ConnectivitySettingsManager.getMobileDataPreferredUids(mContext);
+ mSystemNetworkRequestCounter.transact(
+ mDeps.getCallingUid(), 1 /* numOfNewRequests */,
+ () -> {
+ final ArraySet<NetworkRequestInfo> nris =
+ createNrisFromMobileDataPreferredUids(mMobileDataPreferredUids);
+ replaceDefaultNetworkRequestsForPreference(nris);
+ });
+ // Finally, rematch.
+ rematchAllNetworksAndRequests();
+ }
+
private void enforceAutomotiveDevice() {
final boolean isAutomotiveDevice =
mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
@@ -9796,7 +9887,8 @@
enforceAutomotiveDevice();
enforceOemNetworkPreferencesPermission();
- if (!mProfileNetworkPreferences.isEmpty()) {
+ // TODO: Have a priority for each preference.
+ if (!mProfileNetworkPreferences.isEmpty() || !mMobileDataPreferredUids.isEmpty()) {
// Strictly speaking, mProfileNetworkPreferences should only be touched on the
// handler thread. However it is an immutable object, so reading the reference is
// safe - it's just possible the value is slightly outdated. For the final check,
@@ -9834,7 +9926,8 @@
// The binder call has already checked this, but as mOemNetworkPreferences is only
// touched on the handler thread, it's theoretically not impossible that it has changed
// since.
- if (!mProfileNetworkPreferences.isEmpty()) {
+ // TODO: Have a priority for each preference.
+ if (!mProfileNetworkPreferences.isEmpty() || !mMobileDataPreferredUids.isEmpty()) {
logwtf("handleSetOemPreference, but per-profile network preferences not empty");
return;
}
diff --git a/service/src/com/android/server/connectivity/NetworkOffer.java b/service/src/com/android/server/connectivity/NetworkOffer.java
index fa2d465..548db6b 100644
--- a/service/src/com/android/server/connectivity/NetworkOffer.java
+++ b/service/src/com/android/server/connectivity/NetworkOffer.java
@@ -21,7 +21,6 @@
import android.net.INetworkOfferCallback;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
-import android.os.Messenger;
import java.util.Objects;
@@ -44,7 +43,7 @@
@NonNull public final FullScore score;
@NonNull public final NetworkCapabilities caps;
@NonNull public final INetworkOfferCallback callback;
- @NonNull public final Messenger provider;
+ @NonNull public final int providerId;
private static NetworkCapabilities emptyCaps() {
final NetworkCapabilities nc = new NetworkCapabilities();
@@ -56,11 +55,11 @@
public NetworkOffer(@NonNull final FullScore score,
@Nullable final NetworkCapabilities caps,
@NonNull final INetworkOfferCallback callback,
- @NonNull final Messenger provider) {
+ @NonNull final int providerId) {
this.score = Objects.requireNonNull(score);
this.caps = null != caps ? caps : emptyCaps();
this.callback = Objects.requireNonNull(callback);
- this.provider = Objects.requireNonNull(provider);
+ this.providerId = providerId;
}
/**
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
index 673c804..49b43f8 100644
--- a/service/src/com/android/server/connectivity/PermissionMonitor.java
+++ b/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -24,7 +24,7 @@
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
-import static android.net.ConnectivitySettingsManager.APPS_ALLOWED_ON_RESTRICTED_NETWORKS;
+import static android.net.ConnectivitySettingsManager.UIDS_ALLOWED_ON_RESTRICTED_NETWORKS;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
@@ -109,13 +109,13 @@
@GuardedBy("this")
private final Set<Integer> mAllApps = new HashSet<>();
- // A set of apps which are allowed to use restricted networks. These apps can't hold the
- // CONNECTIVITY_USE_RESTRICTED_NETWORKS permission because they can't be signature|privileged
- // apps. However, these apps should still be able to use restricted networks under certain
- // conditions (e.g. government app using emergency services). So grant netd system permission
- // to uids whose package name is listed in APPS_ALLOWED_ON_RESTRICTED_NETWORKS setting.
+ // A set of uids which are allowed to use restricted networks. The packages of these uids can't
+ // hold the CONNECTIVITY_USE_RESTRICTED_NETWORKS permission because they can't be
+ // signature|privileged apps. However, these apps should still be able to use restricted
+ // networks under certain conditions (e.g. government app using emergency services). So grant
+ // netd system permission to these uids which is listed in UIDS_ALLOWED_ON_RESTRICTED_NETWORKS.
@GuardedBy("this")
- private final Set<String> mAppsAllowedOnRestrictedNetworks = new ArraySet<>();
+ private final Set<Integer> mUidsAllowedOnRestrictedNetworks = new ArraySet<>();
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
@@ -149,10 +149,10 @@
}
/**
- * Get apps allowed to use restricted networks via ConnectivitySettingsManager.
+ * Get uids allowed to use restricted networks via ConnectivitySettingsManager.
*/
- public Set<String> getAppsAllowedOnRestrictedNetworks(@NonNull Context context) {
- return ConnectivitySettingsManager.getAppsAllowedOnRestrictedNetworks(context);
+ public Set<Integer> getUidsAllowedOnRestrictedNetworks(@NonNull Context context) {
+ return ConnectivitySettingsManager.getUidsAllowedOnRestrictedNetworks(context);
}
/**
@@ -194,10 +194,10 @@
mIntentReceiver, intentFilter, null /* broadcastPermission */,
null /* scheduler */);
- // Register APPS_ALLOWED_ON_RESTRICTED_NETWORKS setting observer
+ // Register UIDS_ALLOWED_ON_RESTRICTED_NETWORKS setting observer
mDeps.registerContentObserver(
userAllContext,
- Settings.Secure.getUriFor(APPS_ALLOWED_ON_RESTRICTED_NETWORKS),
+ Settings.Secure.getUriFor(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS),
false /* notifyForDescendants */,
new ContentObserver(null) {
@Override
@@ -206,9 +206,9 @@
}
});
- // Read APPS_ALLOWED_ON_RESTRICTED_NETWORKS setting and update
- // mAppsAllowedOnRestrictedNetworks.
- updateAppsAllowedOnRestrictedNetworks(mDeps.getAppsAllowedOnRestrictedNetworks(mContext));
+ // Read UIDS_ALLOWED_ON_RESTRICTED_NETWORKS setting and update
+ // mUidsAllowedOnRestrictedNetworks.
+ updateUidsAllowedOnRestrictedNetworks(mDeps.getUidsAllowedOnRestrictedNetworks(mContext));
List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS
| MATCH_ANY_USER);
@@ -265,9 +265,9 @@
}
@VisibleForTesting
- void updateAppsAllowedOnRestrictedNetworks(final Set<String> apps) {
- mAppsAllowedOnRestrictedNetworks.clear();
- mAppsAllowedOnRestrictedNetworks.addAll(apps);
+ void updateUidsAllowedOnRestrictedNetworks(final Set<Integer> uids) {
+ mUidsAllowedOnRestrictedNetworks.clear();
+ mUidsAllowedOnRestrictedNetworks.addAll(uids);
}
@VisibleForTesting
@@ -285,10 +285,11 @@
}
@VisibleForTesting
- boolean isAppAllowedOnRestrictedNetworks(@NonNull final PackageInfo app) {
- // Check whether package name is in allowed on restricted networks app list. If so, this app
- // can have netd system permission.
- return mAppsAllowedOnRestrictedNetworks.contains(app.packageName);
+ boolean isUidAllowedOnRestrictedNetworks(final ApplicationInfo appInfo) {
+ if (appInfo == null) return false;
+ // Check whether package's uid is in allowed on restricted networks uid list. If so, this
+ // uid can have netd system permission.
+ return mUidsAllowedOnRestrictedNetworks.contains(appInfo.uid);
}
@VisibleForTesting
@@ -310,7 +311,8 @@
boolean hasRestrictedNetworkPermission(@NonNull final PackageInfo app) {
// TODO : remove carryover package check in the future(b/31479477). All apps should just
// request the appropriate permission for their use case since android Q.
- return isCarryoverPackage(app.applicationInfo) || isAppAllowedOnRestrictedNetworks(app)
+ return isCarryoverPackage(app.applicationInfo)
+ || isUidAllowedOnRestrictedNetworks(app.applicationInfo)
|| hasPermission(app, PERMISSION_MAINLINE_NETWORK_STACK)
|| hasPermission(app, NETWORK_STACK)
|| hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
@@ -770,35 +772,31 @@
}
private synchronized void onSettingChanged() {
- // Step1. Update apps allowed to use restricted networks and compute the set of packages to
+ // Step1. Update uids allowed to use restricted networks and compute the set of uids to
// update.
- final Set<String> packagesToUpdate = new ArraySet<>(mAppsAllowedOnRestrictedNetworks);
- updateAppsAllowedOnRestrictedNetworks(mDeps.getAppsAllowedOnRestrictedNetworks(mContext));
- packagesToUpdate.addAll(mAppsAllowedOnRestrictedNetworks);
+ final Set<Integer> uidsToUpdate = new ArraySet<>(mUidsAllowedOnRestrictedNetworks);
+ updateUidsAllowedOnRestrictedNetworks(mDeps.getUidsAllowedOnRestrictedNetworks(mContext));
+ uidsToUpdate.addAll(mUidsAllowedOnRestrictedNetworks);
- final Map<Integer, Boolean> updatedApps = new HashMap<>();
- final Map<Integer, Boolean> removedApps = new HashMap<>();
+ final Map<Integer, Boolean> updatedUids = new HashMap<>();
+ final Map<Integer, Boolean> removedUids = new HashMap<>();
- // Step2. For each package to update, find out its new permission.
- for (String app : packagesToUpdate) {
- final PackageInfo info = getPackageInfo(app);
- if (info == null || info.applicationInfo == null) continue;
-
- final int uid = info.applicationInfo.uid;
+ // Step2. For each uid to update, find out its new permission.
+ for (Integer uid : uidsToUpdate) {
final Boolean permission = highestUidNetworkPermission(uid);
if (null == permission) {
- removedApps.put(uid, NETWORK); // Doesn't matter which permission is set here.
+ removedUids.put(uid, NETWORK); // Doesn't matter which permission is set here.
mApps.remove(uid);
} else {
- updatedApps.put(uid, permission);
+ updatedUids.put(uid, permission);
mApps.put(uid, permission);
}
}
// Step3. Update or revoke permission for uids with netd.
- update(mUsers, updatedApps, true /* add */);
- update(mUsers, removedApps, false /* add */);
+ update(mUsers, updatedUids, true /* add */);
+ update(mUsers, removedUids, false /* add */);
}
/** Dump info to dumpsys */
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/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 04e63fc..0d3cd61 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -299,7 +299,9 @@
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.net.module.util.ArrayTrackRecord;
+import com.android.net.module.util.CollectionUtils;
import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
+import com.android.server.ConnectivityService.NetworkRequestInfo;
import com.android.server.connectivity.ConnectivityConstants;
import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.Nat464Xlat;
@@ -415,6 +417,7 @@
private static final String VPN_IFNAME = "tun10042";
private static final String TEST_PACKAGE_NAME = "com.android.test.package";
private static final int TEST_PACKAGE_UID = 123;
+ private static final int TEST_PACKAGE_UID2 = 321;
private static final String ALWAYS_ON_PACKAGE = "com.android.test.alwaysonvpn";
private static final String INTERFACE_NAME = "interface";
@@ -1170,6 +1173,10 @@
return ranges;
}
+ private Set<UidRange> uidRangesForUids(Collection<Integer> uids) {
+ return uidRangesForUids(CollectionUtils.toIntArray(uids));
+ }
+
private static Looper startHandlerThreadAndReturnLooper() {
final HandlerThread handlerThread = new HandlerThread("MockVpnThread");
handlerThread.start();
@@ -1536,6 +1543,8 @@
private static final UserInfo PRIMARY_USER_INFO = new UserInfo(PRIMARY_USER, "",
UserInfo.FLAG_PRIMARY);
private static final UserHandle PRIMARY_USER_HANDLE = new UserHandle(PRIMARY_USER);
+ private static final int SECONDARY_USER = 10;
+ private static final UserHandle SECONDARY_USER_HANDLE = new UserHandle(SECONDARY_USER);
private static final int RESTRICTED_USER = 1;
private static final UserInfo RESTRICTED_USER_INFO = new UserInfo(RESTRICTED_USER, "",
@@ -10050,7 +10059,7 @@
mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
waitForIdle();
- final ConnectivityService.NetworkRequestInfo[] nriOutput = mService.requestsSortedById();
+ final NetworkRequestInfo[] nriOutput = mService.requestsSortedById();
assertTrue(nriOutput.length > 1);
for (int i = 0; i < nriOutput.length - 1; i++) {
@@ -10333,8 +10342,7 @@
.thenReturn(hasFeature);
}
- private Range<Integer> getNriFirstUidRange(
- @NonNull final ConnectivityService.NetworkRequestInfo nri) {
+ private Range<Integer> getNriFirstUidRange(@NonNull final NetworkRequestInfo nri) {
return nri.mRequests.get(0).networkCapabilities.getUids().iterator().next();
}
@@ -10373,7 +10381,7 @@
OEM_NETWORK_PREFERENCE_OEM_PAID;
// Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences()
- final ArraySet<ConnectivityService.NetworkRequestInfo> nris =
+ final ArraySet<NetworkRequestInfo> nris =
mService.new OemNetworkRequestFactory()
.createNrisFromOemNetworkPreferences(
createDefaultOemNetworkPreferences(prefToTest));
@@ -10402,7 +10410,7 @@
OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK;
// Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences()
- final ArraySet<ConnectivityService.NetworkRequestInfo> nris =
+ final ArraySet<NetworkRequestInfo> nris =
mService.new OemNetworkRequestFactory()
.createNrisFromOemNetworkPreferences(
createDefaultOemNetworkPreferences(prefToTest));
@@ -10428,7 +10436,7 @@
OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
// Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences()
- final ArraySet<ConnectivityService.NetworkRequestInfo> nris =
+ final ArraySet<NetworkRequestInfo> nris =
mService.new OemNetworkRequestFactory()
.createNrisFromOemNetworkPreferences(
createDefaultOemNetworkPreferences(prefToTest));
@@ -10451,7 +10459,7 @@
OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
// Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences()
- final ArraySet<ConnectivityService.NetworkRequestInfo> nris =
+ final ArraySet<NetworkRequestInfo> nris =
mService.new OemNetworkRequestFactory()
.createNrisFromOemNetworkPreferences(
createDefaultOemNetworkPreferences(prefToTest));
@@ -10484,7 +10492,7 @@
.build();
// Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences()
- final ArraySet<ConnectivityService.NetworkRequestInfo> nris =
+ final ArraySet<NetworkRequestInfo> nris =
mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(pref);
assertNotNull(nris);
@@ -10509,7 +10517,7 @@
.build();
// Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences()
- final List<ConnectivityService.NetworkRequestInfo> nris =
+ final List<NetworkRequestInfo> nris =
new ArrayList<>(
mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(
pref));
@@ -10541,7 +10549,7 @@
.build();
// Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences()
- final List<ConnectivityService.NetworkRequestInfo> nris =
+ final List<NetworkRequestInfo> nris =
new ArrayList<>(
mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(
pref));
@@ -10583,7 +10591,7 @@
.build();
// Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences()
- final ArraySet<ConnectivityService.NetworkRequestInfo> nris =
+ final ArraySet<NetworkRequestInfo> nris =
mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(pref);
assertEquals(expectedNumOfNris, nris.size());
@@ -10676,8 +10684,7 @@
// each time to confirm it doesn't change under test.
final int expectedDefaultNetworkRequestsSize = 2;
assertEquals(expectedDefaultNetworkRequestsSize, mService.mDefaultNetworkRequests.size());
- for (final ConnectivityService.NetworkRequestInfo defaultRequest
- : mService.mDefaultNetworkRequests) {
+ for (final NetworkRequestInfo defaultRequest : mService.mDefaultNetworkRequests) {
final Network defaultNetwork = defaultRequest.getSatisfier() == null
? null : defaultRequest.getSatisfier().network();
// If this is the default request.
@@ -12506,4 +12513,73 @@
}
}
}
+
+ private void assertCreateNrisFromMobileDataPreferredUids(Set<Integer> uids) {
+ final Set<NetworkRequestInfo> nris =
+ mService.createNrisFromMobileDataPreferredUids(uids);
+ final NetworkRequestInfo nri = nris.iterator().next();
+ // Verify that one NRI is created with multilayer requests. Because one NRI can contain
+ // multiple uid ranges, so it only need create one NRI here.
+ assertEquals(1, nris.size());
+ assertTrue(nri.isMultilayerRequest());
+ assertEquals(nri.getUids(), uidRangesForUids(uids));
+ }
+
+ /**
+ * Test createNrisFromMobileDataPreferredUids returns correct NetworkRequestInfo.
+ */
+ @Test
+ public void testCreateNrisFromMobileDataPreferredUids() {
+ // Verify that empty uid set should not create any NRI for it.
+ final Set<NetworkRequestInfo> nrisNoUid =
+ mService.createNrisFromMobileDataPreferredUids(new ArraySet<>());
+ assertEquals(0, nrisNoUid.size());
+
+ final int uid1 = PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID);
+ final int uid2 = PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID2);
+ final int uid3 = SECONDARY_USER_HANDLE.getUid(TEST_PACKAGE_UID);
+ assertCreateNrisFromMobileDataPreferredUids(Set.of(uid1));
+ assertCreateNrisFromMobileDataPreferredUids(Set.of(uid1, uid3));
+ assertCreateNrisFromMobileDataPreferredUids(Set.of(uid1, uid2));
+ }
+
+ private void setAndUpdateMobileDataPreferredUids(Set<Integer> uids) {
+ ConnectivitySettingsManager.setMobileDataPreferredUids(mServiceContext, uids);
+ mService.updateMobileDataPreferredUids();
+ waitForIdle();
+ }
+
+ /**
+ * Test that MOBILE_DATA_PREFERRED_UIDS changes will send correct net id and uid ranges to netd.
+ */
+ @Test
+ public void testMobileDataPreferredUidsChanged() throws Exception {
+ final InOrder inorder = inOrder(mMockNetd);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ waitForIdle();
+
+ final int cellNetId = mCellNetworkAgent.getNetwork().netId;
+ inorder.verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(
+ cellNetId, INetd.PERMISSION_NONE));
+
+ // Initial mobile data preferred uids status.
+ setAndUpdateMobileDataPreferredUids(Set.of());
+ inorder.verify(mMockNetd, never()).networkAddUidRanges(anyInt(), any());
+ inorder.verify(mMockNetd, never()).networkRemoveUidRanges(anyInt(), any());
+
+ final Set<Integer> uids1 = Set.of(PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID));
+ final UidRangeParcel[] uidRanges1 = toUidRangeStableParcels(uidRangesForUids(uids1));
+ setAndUpdateMobileDataPreferredUids(uids1);
+ inorder.verify(mMockNetd, times(1)).networkAddUidRanges(cellNetId, uidRanges1);
+ inorder.verify(mMockNetd, never()).networkRemoveUidRanges(anyInt(), any());
+
+ final Set<Integer> uids2 = Set.of(PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID),
+ PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID2),
+ SECONDARY_USER_HANDLE.getUid(TEST_PACKAGE_UID));
+ final UidRangeParcel[] uidRanges2 = toUidRangeStableParcels(uidRangesForUids(uids2));
+ setAndUpdateMobileDataPreferredUids(uids2);
+ inorder.verify(mMockNetd, times(1)).networkRemoveUidRanges(cellNetId, uidRanges1);
+ inorder.verify(mMockNetd, times(1)).networkAddUidRanges(cellNetId, uidRanges2);
+ }
}
diff --git a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
index c75618f..c6e7606 100644
--- a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -30,7 +30,7 @@
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_REQUIRED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
-import static android.net.ConnectivitySettingsManager.APPS_ALLOWED_ON_RESTRICTED_NETWORKS;
+import static android.net.ConnectivitySettingsManager.UIDS_ALLOWED_ON_RESTRICTED_NETWORKS;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.os.Process.SYSTEM_UID;
@@ -142,7 +142,7 @@
final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext));
doReturn(UserHandle.ALL).when(asUserCtx).getUser();
when(mContext.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx);
- when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());
+ when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());
mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps));
@@ -341,9 +341,9 @@
}
@Test
- public void testHasRestrictedNetworkPermissionAppAllowedOnRestrictedNetworks() {
- mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks(
- new ArraySet<>(new String[] { MOCK_PACKAGE1 }));
+ public void testHasRestrictedNetworkPermissionUidAllowedOnRestrictedNetworks() {
+ mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(
+ new ArraySet<>(new Integer[] { MOCK_UID1 }));
assertTrue(hasRestrictedNetworkPermission(
PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1));
assertTrue(hasRestrictedNetworkPermission(
@@ -352,11 +352,11 @@
PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1, CONNECTIVITY_INTERNAL));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID1));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID2));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID1, CHANGE_NETWORK_STATE));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID2, CHANGE_NETWORK_STATE));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID2, CONNECTIVITY_INTERNAL));
}
@@ -396,32 +396,32 @@
assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, MOCK_UID1));
}
- private boolean wouldBeAppAllowedOnRestrictedNetworks(String packageName) {
- final PackageInfo packageInfo = new PackageInfo();
- packageInfo.packageName = packageName;
- return mPermissionMonitor.isAppAllowedOnRestrictedNetworks(packageInfo);
+ private boolean wouldBeUidAllowedOnRestrictedNetworks(int uid) {
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.uid = uid;
+ return mPermissionMonitor.isUidAllowedOnRestrictedNetworks(applicationInfo);
}
@Test
public void testIsAppAllowedOnRestrictedNetworks() {
- mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks(new ArraySet<>());
- assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE1));
- assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE2));
+ mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(new ArraySet<>());
+ assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
+ assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
- mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks(
- new ArraySet<>(new String[] { MOCK_PACKAGE1 }));
- assertTrue(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE1));
- assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE2));
+ mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(
+ new ArraySet<>(new Integer[] { MOCK_UID1 }));
+ assertTrue(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
+ assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
- mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks(
- new ArraySet<>(new String[] { MOCK_PACKAGE2 }));
- assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE1));
- assertTrue(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE2));
+ mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(
+ new ArraySet<>(new Integer[] { MOCK_UID2 }));
+ assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
+ assertTrue(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
- mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks(
- new ArraySet<>(new String[] { "com.android.test" }));
- assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE1));
- assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE2));
+ mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(
+ new ArraySet<>(new Integer[] { 123 }));
+ assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
+ assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
}
private void assertBackgroundPermission(boolean hasPermission, String name, int uid,
@@ -901,12 +901,12 @@
}
@Test
- public void testAppsAllowedOnRestrictedNetworksChanged() throws Exception {
+ public void testUidsAllowedOnRestrictedNetworksChanged() throws Exception {
final NetdMonitor mNetdMonitor = new NetdMonitor(mNetdService);
final ArgumentCaptor<ContentObserver> captor =
ArgumentCaptor.forClass(ContentObserver.class);
verify(mDeps, times(1)).registerContentObserver(any(),
- argThat(uri -> uri.getEncodedPath().contains(APPS_ALLOWED_ON_RESTRICTED_NETWORKS)),
+ argThat(uri -> uri.getEncodedPath().contains(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS)),
anyBoolean(), captor.capture());
final ContentObserver contentObserver = captor.getValue();
@@ -924,24 +924,24 @@
when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2);
when(mPackageManager.getPackagesForUid(MOCK_UID2)).thenReturn(new String[]{MOCK_PACKAGE2});
- // MOCK_PACKAGE1 is listed in setting that allow to use restricted networks, MOCK_UID1
+ // MOCK_UID1 is listed in setting that allow to use restricted networks, MOCK_UID1
// should have SYSTEM permission.
- when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(
- new ArraySet<>(new String[] { MOCK_PACKAGE1 }));
+ when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(
+ new ArraySet<>(new Integer[] { MOCK_UID1 }));
contentObserver.onChange(true /* selfChange */);
mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID2});
- // MOCK_PACKAGE2 is listed in setting that allow to use restricted networks, MOCK_UID2
+ // MOCK_UID2 is listed in setting that allow to use restricted networks, MOCK_UID2
// should have SYSTEM permission but MOCK_UID1 should revoke permission.
- when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(
- new ArraySet<>(new String[] { MOCK_PACKAGE2 }));
+ when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(
+ new ArraySet<>(new Integer[] { MOCK_UID2 }));
contentObserver.onChange(true /* selfChange */);
mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID2});
mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
- // No app lists in setting, should revoke permission from all uids.
- when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());
+ // No uid lists in setting, should revoke permission from all uids.
+ when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());
contentObserver.onChange(true /* selfChange */);
mNetdMonitor.expectNoPermission(
new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1, MOCK_UID2});
@@ -953,7 +953,7 @@
final ArgumentCaptor<ContentObserver> captor =
ArgumentCaptor.forClass(ContentObserver.class);
verify(mDeps, times(1)).registerContentObserver(any(),
- argThat(uri -> uri.getEncodedPath().contains(APPS_ALLOWED_ON_RESTRICTED_NETWORKS)),
+ argThat(uri -> uri.getEncodedPath().contains(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS)),
anyBoolean(), captor.capture());
final ContentObserver contentObserver = captor.getValue();
@@ -974,22 +974,15 @@
addPackageForUsers(new UserHandle[]{MOCK_USER1}, MOCK_PACKAGE1, MOCK_UID1);
mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
- // MOCK_PACKAGE2 is listed in setting that allow to use restricted networks, MOCK_UID1
+ // MOCK_UID1 is listed in setting that allow to use restricted networks, MOCK_UID1
// should upgrade to SYSTEM permission.
- when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(
- new ArraySet<>(new String[] { MOCK_PACKAGE2 }));
- contentObserver.onChange(true /* selfChange */);
- mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
-
- // MOCK_PACKAGE1 is listed in setting that allow to use restricted networks, MOCK_UID1
- // should still have SYSTEM permission.
- when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(
- new ArraySet<>(new String[] { MOCK_PACKAGE1 }));
+ when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(
+ new ArraySet<>(new Integer[] { MOCK_UID1 }));
contentObserver.onChange(true /* selfChange */);
mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
// No app lists in setting, MOCK_UID1 should downgrade to NETWORK permission.
- when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());
+ when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());
contentObserver.onChange(true /* selfChange */);
mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
@@ -998,4 +991,4 @@
removePackageForUsers(new UserHandle[]{MOCK_USER1}, MOCK_PACKAGE1, MOCK_UID1);
mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
}
-}
\ No newline at end of file
+}