Merge "netd.c: handle overflowuid in bpf_owner_match()" into main
diff --git a/framework/api/current.txt b/framework/api/current.txt
index 6860c3c..ef8415c 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -315,6 +315,7 @@
method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
method public int getOwnerUid();
method public int getSignalStrength();
+ method @FlaggedApi("com.android.net.flags.request_restricted_wifi") @NonNull public java.util.Set<java.lang.Integer> getSubscriptionIds();
method @Nullable public android.net.TransportInfo getTransportInfo();
method public boolean hasCapability(int);
method public boolean hasEnterpriseId(int);
@@ -332,6 +333,7 @@
field public static final int NET_CAPABILITY_IA = 7; // 0x7
field public static final int NET_CAPABILITY_IMS = 4; // 0x4
field public static final int NET_CAPABILITY_INTERNET = 12; // 0xc
+ field @FlaggedApi("com.android.net.flags.net_capability_local_network") public static final int NET_CAPABILITY_LOCAL_NETWORK = 36; // 0x24
field public static final int NET_CAPABILITY_MCX = 23; // 0x17
field public static final int NET_CAPABILITY_MMS = 0; // 0x0
field public static final int NET_CAPABILITY_MMTEL = 33; // 0x21
@@ -360,6 +362,7 @@
field public static final int TRANSPORT_CELLULAR = 0; // 0x0
field public static final int TRANSPORT_ETHERNET = 3; // 0x3
field public static final int TRANSPORT_LOWPAN = 6; // 0x6
+ field @FlaggedApi("com.android.net.flags.support_transport_satellite") public static final int TRANSPORT_SATELLITE = 10; // 0xa
field public static final int TRANSPORT_THREAD = 9; // 0x9
field public static final int TRANSPORT_USB = 8; // 0x8
field public static final int TRANSPORT_VPN = 4; // 0x4
@@ -418,6 +421,7 @@
method public int describeContents();
method @NonNull public int[] getCapabilities();
method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
+ method @FlaggedApi("com.android.net.flags.request_restricted_wifi") @NonNull public java.util.Set<java.lang.Integer> getSubscriptionIds();
method @NonNull public int[] getTransportTypes();
method public boolean hasCapability(int);
method public boolean hasTransport(int);
@@ -437,6 +441,7 @@
method @NonNull public android.net.NetworkRequest.Builder setIncludeOtherUidNetworks(boolean);
method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
+ method @FlaggedApi("com.android.net.flags.request_restricted_wifi") @NonNull public android.net.NetworkRequest.Builder setSubscriptionIds(@NonNull java.util.Set<java.lang.Integer>);
}
public class ParseException extends java.lang.RuntimeException {
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index e812024..bef29a4 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -307,7 +307,6 @@
method @NonNull public int[] getAdministratorUids();
method @Nullable public static String getCapabilityCarrierName(int);
method @Nullable public String getSsid();
- method @NonNull public java.util.Set<java.lang.Integer> getSubscriptionIds();
method @NonNull public int[] getTransportTypes();
method @Nullable public java.util.List<android.net.Network> getUnderlyingNetworks();
method public boolean isPrivateDnsBroken();
@@ -373,7 +372,6 @@
public static class NetworkRequest.Builder {
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int);
- method @NonNull public android.net.NetworkRequest.Builder setSubscriptionIds(@NonNull java.util.Set<java.lang.Integer>);
}
public final class NetworkScore implements android.os.Parcelable {
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index efae754..140a631 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -20,6 +20,7 @@
import static com.android.net.module.util.BitUtils.appendStringRepresentationOfBitMaskToStringBuilder;
import static com.android.net.module.util.BitUtils.describeDifferences;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.LongDef;
import android.annotation.NonNull;
@@ -29,9 +30,6 @@
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.net.ConnectivityManager.NetworkCallback;
-// Can't be imported because aconfig tooling doesn't exist on udc-mainline-prod yet
-// See inner class Flags which mimics this for the time being
-// import android.net.flags.Flags;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -130,6 +128,12 @@
public static class Flags {
static final String FLAG_FORBIDDEN_CAPABILITY =
"com.android.net.flags.forbidden_capability";
+ static final String FLAG_NET_CAPABILITY_LOCAL_NETWORK =
+ "com.android.net.flags.net_capability_local_network";
+ static final String REQUEST_RESTRICTED_WIFI =
+ "com.android.net.flags.request_restricted_wifi";
+ static final String SUPPORT_TRANSPORT_SATELLITE =
+ "com.android.net.flags.support_transport_satellite";
}
/**
@@ -716,17 +720,13 @@
public static final int NET_CAPABILITY_PRIORITIZE_BANDWIDTH = 35;
/**
- * This is a local network, e.g. a tethering downstream or a P2P direct network.
+ * Indicates that this network is a local network, e.g. thread network
*
- * <p>
- * Note that local networks are not sent to callbacks by default. To receive callbacks about
- * them, the {@link NetworkRequest} instance must be prepared to see them, either by
- * adding the capability with {@link NetworkRequest.Builder#addCapability}, by removing
- * this forbidden capability with {@link NetworkRequest.Builder#removeForbiddenCapability},
- * or by clearing all capabilites with {@link NetworkRequest.Builder#clearCapabilities()}.
- * </p>
- * @hide
+ * Apps that target an SDK before {@link Build.VERSION_CODES.VANILLA_ICE_CREAM} will not see
+ * networks with this capability unless they explicitly set the NET_CAPABILITY_LOCAL_NETWORK
+ * in their NetworkRequests.
*/
+ @FlaggedApi(Flags.FLAG_NET_CAPABILITY_LOCAL_NETWORK)
public static final int NET_CAPABILITY_LOCAL_NETWORK = 36;
private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_LOCAL_NETWORK;
@@ -1257,6 +1257,7 @@
TRANSPORT_TEST,
TRANSPORT_USB,
TRANSPORT_THREAD,
+ TRANSPORT_SATELLITE,
})
public @interface Transport { }
@@ -1313,10 +1314,16 @@
*/
public static final int TRANSPORT_THREAD = 9;
+ /**
+ * Indicates this network uses a Satellite transport.
+ */
+ @FlaggedApi(Flags.SUPPORT_TRANSPORT_SATELLITE)
+ public static final int TRANSPORT_SATELLITE = 10;
+
/** @hide */
public static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
/** @hide */
- public static final int MAX_TRANSPORT = TRANSPORT_THREAD;
+ public static final int MAX_TRANSPORT = TRANSPORT_SATELLITE;
private static final int ALL_VALID_TRANSPORTS;
static {
@@ -1343,6 +1350,7 @@
"TEST",
"USB",
"THREAD",
+ "SATELLITE",
};
/**
@@ -2794,10 +2802,9 @@
* receiver holds the NETWORK_FACTORY permission. In all other cases, it will be the empty set.
*
* @return
- * @hide
*/
@NonNull
- @SystemApi
+ @FlaggedApi(Flags.REQUEST_RESTRICTED_WIFI)
public Set<Integer> getSubscriptionIds() {
return new ArraySet<>(mSubIds);
}
diff --git a/framework/src/android/net/NetworkRequest.java b/framework/src/android/net/NetworkRequest.java
index 653e41d..4de02ac 100644
--- a/framework/src/android/net/NetworkRequest.java
+++ b/framework/src/android/net/NetworkRequest.java
@@ -34,6 +34,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -145,6 +146,12 @@
* Look up the specific capability to learn whether its usage requires this self-certification.
*/
public class NetworkRequest implements Parcelable {
+
+ /** @hide */
+ public static class Flags {
+ static final String REQUEST_RESTRICTED_WIFI =
+ "com.android.net.flags.request_restricted_wifi";
+ }
/**
* The first requestId value that will be allocated.
* @hide only used by ConnectivityService.
@@ -630,10 +637,9 @@
* NETWORK_FACTORY permission.
*
* @param subIds A {@code Set} that represents subscription IDs.
- * @hide
*/
@NonNull
- @SystemApi
+ @FlaggedApi(Flags.REQUEST_RESTRICTED_WIFI)
public Builder setSubscriptionIds(@NonNull Set<Integer> subIds) {
mNetworkCapabilities.setSubscriptionIds(subIds);
return this;
@@ -890,4 +896,17 @@
// a new array.
return networkCapabilities.getTransportTypes();
}
+
+ /**
+ * Gets all the subscription ids set on this {@code NetworkRequest} instance.
+ *
+ * @return Set of Integer values for this instance.
+ */
+ @NonNull
+ @FlaggedApi(Flags.REQUEST_RESTRICTED_WIFI)
+ public Set<Integer> getSubscriptionIds() {
+ // No need to make a defensive copy here as NC#getSubscriptionIds() already returns
+ // a new set.
+ return networkCapabilities.getSubscriptionIds();
+ }
}
diff --git a/service/Android.bp b/service/Android.bp
index c6ecadd..403ba7d 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -179,6 +179,8 @@
"unsupportedappusage",
"ServiceConnectivityResources",
"framework-statsd",
+ "framework-permission",
+ "framework-permission-s",
],
static_libs: [
// Do not add libs here if they are already included
@@ -186,7 +188,7 @@
"androidx.annotation_annotation",
"connectivity-net-module-utils-bpf",
"connectivity_native_aidl_interface-lateststable-java",
- "dnsresolver_aidl_interface-V13-java",
+ "dnsresolver_aidl_interface-V14-java",
"modules-utils-shell-command-handler",
"net-utils-device-common",
"net-utils-device-common-ip",
@@ -265,6 +267,8 @@
"framework-tethering.impl",
"framework-wifi",
"libprotobuf-java-nano",
+ "framework-permission",
+ "framework-permission-s",
],
jarjar_rules: ":connectivity-jarjar-rules",
apex_available: [
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 52f890d..1c92488 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -113,6 +113,8 @@
import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPermissionOr;
import static com.android.net.module.util.PermissionUtils.hasAnyPermissionOf;
import static com.android.server.ConnectivityStatsLog.CONNECTIVITY_STATE_SAMPLE;
+import static com.android.server.connectivity.CarrierPrivilegeAuthenticator.CarrierPrivilegesLostListener;
+import static com.android.server.connectivity.ConnectivityFlags.REQUEST_RESTRICTED_WIFI;
import android.Manifest;
import android.annotation.CheckResult;
@@ -336,6 +338,7 @@
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.QosCallbackTracker;
import com.android.server.connectivity.RoutingCoordinatorService;
+import com.android.server.connectivity.SatelliteAccessController;
import com.android.server.connectivity.UidRangeUtils;
import com.android.server.connectivity.VpnNetworkPreferenceInfo;
import com.android.server.connectivity.wear.CompanionDeviceManagerProxyService;
@@ -373,6 +376,7 @@
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
/**
* @hide
@@ -467,6 +471,8 @@
private volatile boolean mLockdownEnabled;
+ private final boolean mRequestRestrictedWifiEnabled;
+
/**
* Stale copy of uid blocked reasons provided by NPMS. As long as they are accessed only in
* internal handler thread, they don't need a lock.
@@ -562,6 +568,10 @@
// See {@link ConnectivitySettingsManager#setMobileDataPreferredUids}
@VisibleForTesting
static final int PREFERENCE_ORDER_MOBILE_DATA_PREFERERRED = 30;
+ // Order of setting satellite network preference fallback when default message application
+ // with role_sms role and android.permission.SATELLITE_COMMUNICATION permission detected
+ @VisibleForTesting
+ static final int PREFERENCE_ORDER_SATELLITE_FALLBACK = 40;
// Preference order that signifies the network shouldn't be set as a default network for
// the UIDs, only give them access to it. TODO : replace this with a boolean
// in NativeUidRangeConfig
@@ -832,6 +842,11 @@
private static final int EVENT_UID_FROZEN_STATE_CHANGED = 61;
/**
+ * Event to inform the ConnectivityService handler when a uid has lost carrier privileges.
+ */
+ private static final int EVENT_UID_CARRIER_PRIVILEGES_LOST = 62;
+
+ /**
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
* should be shown.
*/
@@ -920,6 +935,7 @@
private final QosCallbackTracker mQosCallbackTracker;
private final NetworkNotificationManager mNotifier;
private final LingerMonitor mLingerMonitor;
+ private final SatelliteAccessController mSatelliteAccessController;
// sequence number of NetworkRequests
private int mNextNetworkRequestId = NetworkRequest.FIRST_REQUEST_ID;
@@ -1270,6 +1286,18 @@
}
private final LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker(this);
+ private final CarrierPrivilegesLostListenerImpl mCarrierPrivilegesLostListenerImpl =
+ new CarrierPrivilegesLostListenerImpl();
+
+ private class CarrierPrivilegesLostListenerImpl implements CarrierPrivilegesLostListener {
+ @Override
+ public void onCarrierPrivilegesLost(int uid) {
+ if (mRequestRestrictedWifiEnabled) {
+ mHandler.sendMessage(mHandler.obtainMessage(
+ EVENT_UID_CARRIER_PRIVILEGES_LOST, uid, 0 /* arg2 */));
+ }
+ }
+ }
final LocalPriorityDump mPriorityDumper = new LocalPriorityDump();
/**
* Helper class which parses out priority arguments and dumps sections according to their
@@ -1328,6 +1356,11 @@
}
}
+ @VisibleForTesting
+ CarrierPrivilegesLostListener getCarrierPrivilegesLostListener() {
+ return mCarrierPrivilegesLostListenerImpl;
+ }
+
/**
* Dependencies of ConnectivityService, for injection in tests.
*/
@@ -1488,15 +1521,31 @@
*/
@Nullable
public CarrierPrivilegeAuthenticator makeCarrierPrivilegeAuthenticator(
- @NonNull final Context context, @NonNull final TelephonyManager tm) {
+ @NonNull final Context context,
+ @NonNull final TelephonyManager tm,
+ boolean requestRestrictedWifiEnabled,
+ @NonNull CarrierPrivilegesLostListener listener) {
if (isAtLeastT()) {
- return new CarrierPrivilegeAuthenticator(context, tm);
+ return new CarrierPrivilegeAuthenticator(
+ context, tm, requestRestrictedWifiEnabled, listener);
} else {
return null;
}
}
/**
+ * @see SatelliteAccessController
+ */
+ @Nullable
+ public SatelliteAccessController makeSatelliteAccessController(
+ @NonNull final Context context,
+ Consumer<Set<Integer>> updateSatelliteNetworkFallbackUidCallback,
+ @NonNull final Handler connectivityServiceInternalHandler) {
+ return new SatelliteAccessController(context, updateSatelliteNetworkFallbackUidCallback,
+ connectivityServiceInternalHandler);
+ }
+
+ /**
* @see DeviceConfigUtils#isTetheringFeatureEnabled
*/
public boolean isFeatureEnabled(Context context, String name) {
@@ -1759,8 +1808,20 @@
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mLocationPermissionChecker = mDeps.makeLocationPermissionChecker(mContext);
- mCarrierPrivilegeAuthenticator =
- mDeps.makeCarrierPrivilegeAuthenticator(mContext, mTelephonyManager);
+ mRequestRestrictedWifiEnabled = mDeps.isAtLeastU()
+ && mDeps.isFeatureEnabled(context, REQUEST_RESTRICTED_WIFI);
+ mCarrierPrivilegeAuthenticator = mDeps.makeCarrierPrivilegeAuthenticator(
+ mContext, mTelephonyManager, mRequestRestrictedWifiEnabled,
+ mCarrierPrivilegesLostListenerImpl);
+
+ if (mDeps.isAtLeastU()
+ && mDeps
+ .isFeatureNotChickenedOut(mContext, ALLOW_SATALLITE_NETWORK_FALLBACK)) {
+ mSatelliteAccessController = mDeps.makeSatelliteAccessController(
+ mContext, this::updateSatelliteNetworkPreferenceUids, mHandler);
+ } else {
+ mSatelliteAccessController = null;
+ }
// To ensure uid state is synchronized with Network Policy, register for
// NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService
@@ -2030,6 +2091,18 @@
new Pair<>(network, proxyInfo)).sendToTarget();
}
+ /**
+ * Called when satellite network fallback uids at {@link SatelliteAccessController}
+ * cache was updated based on {@link
+ * android.app.role.OnRoleHoldersChangedListener#onRoleHoldersChanged(String, UserHandle)},
+ * to create multilayer request with preference order
+ * {@link #PREFERENCE_ORDER_SATELLITE_FALLBACK} there on.
+ *
+ */
+ private void updateSatelliteNetworkPreferenceUids(Set<Integer> satelliteNetworkFallbackUids) {
+ handleSetSatelliteNetworkPreference(satelliteNetworkFallbackUids);
+ }
+
private void handleAlwaysOnNetworkRequest(
NetworkRequest networkRequest, String settingName, boolean defaultValue) {
final boolean enable = toBool(Settings.Global.getInt(
@@ -3372,6 +3445,9 @@
public static final String LOG_BPF_RC = "log_bpf_rc_force_disable";
+ public static final String ALLOW_SATALLITE_NETWORK_FALLBACK =
+ "allow_satallite_network_fallback";
+
private void enforceInternetPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERNET,
@@ -3738,6 +3814,10 @@
updateMobileDataPreferredUids();
}
+ if (mSatelliteAccessController != null) {
+ mSatelliteAccessController.start();
+ }
+
// On T+ devices, register callback for statsd to pull NETWORK_BPF_MAP_INFO atom
if (mDeps.isAtLeastT()) {
mBpfNetMaps.setPullAtomCallback(mContext);
@@ -6410,6 +6490,9 @@
UidFrozenStateChangedArgs args = (UidFrozenStateChangedArgs) msg.obj;
handleFrozenUids(args.mUids, args.mFrozenStates);
break;
+ case EVENT_UID_CARRIER_PRIVILEGES_LOST:
+ handleUidCarrierPrivilegesLost(msg.arg1);
+ break;
}
}
}
@@ -7490,9 +7573,14 @@
}
mAppOpsManager.checkPackage(callerUid, callerPackageName);
- if (!nc.getSubscriptionIds().isEmpty()) {
- enforceNetworkFactoryPermission();
+ if (nc.getSubscriptionIds().isEmpty()) {
+ return;
}
+ if (mRequestRestrictedWifiEnabled
+ && canRequestRestrictedNetworkDueToCarrierPrivileges(nc, callerUid)) {
+ return;
+ }
+ enforceNetworkFactoryPermission();
}
private int[] getSignalStrengthThresholds(@NonNull final NetworkAgentInfo nai) {
@@ -7772,6 +7860,22 @@
applicationNetworkCapabilities.enforceSelfCertifiedNetworkCapabilitiesDeclared(
networkCapabilities);
}
+
+ private boolean canRequestRestrictedNetworkDueToCarrierPrivileges(
+ NetworkCapabilities networkCapabilities, int callingUid) {
+ if (mRequestRestrictedWifiEnabled) {
+ // For U+ devices, callers with carrier privilege could request restricted networks
+ // with CBS capabilities, or any restricted WiFi networks.
+ return ((networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)
+ || networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ && hasCarrierPrivilegeForNetworkCaps(callingUid, networkCapabilities));
+ } else {
+ // For T+ devices, callers with carrier privilege could request with CBS
+ // capabilities.
+ return (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)
+ && hasCarrierPrivilegeForNetworkCaps(callingUid, networkCapabilities));
+ }
+ }
private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities,
String callingPackageName, String callingAttributionTag, final int callingUid) {
if (shouldCheckCapabilitiesDeclaration(networkCapabilities, callingUid,
@@ -7779,13 +7883,11 @@
enforceRequestCapabilitiesDeclaration(callingPackageName, networkCapabilities,
callingUid);
}
- if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) == false) {
- // For T+ devices, callers with carrier privilege could request with CBS capabilities.
- if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)
- && hasCarrierPrivilegeForNetworkCaps(callingUid, networkCapabilities)) {
- return;
+ if (!networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
+ if (!canRequestRestrictedNetworkDueToCarrierPrivileges(
+ networkCapabilities, callingUid)) {
+ enforceConnectivityRestrictedNetworksPermission(true /* checkUidsAllowedList */);
}
- enforceConnectivityRestrictedNetworksPermission(true /* checkUidsAllowedList */);
} else {
enforceChangePermission(callingPackageName, callingAttributionTag);
}
@@ -9052,6 +9154,38 @@
}
}
+ private void handleUidCarrierPrivilegesLost(int uid) {
+ ensureRunningOnConnectivityServiceThread();
+ // A NetworkRequest needs to be revoked when all the conditions are met
+ // 1. It requests restricted network
+ // 2. The requestor uid matches the uid with the callback
+ // 3. The app doesn't have Carrier Privileges
+ // 4. The app doesn't have permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS
+ for (final NetworkRequest nr : mNetworkRequests.keySet()) {
+ if ((nr.isRequest() || nr.isListen())
+ && !nr.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ && nr.getRequestorUid() == uid
+ && !hasConnectivityRestrictedNetworksPermission(uid, true)) {
+ declareNetworkRequestUnfulfillable(nr);
+ }
+ }
+
+ // A NetworkAgent's allowedUids may need to be updated if the app has lost
+ // carrier config
+ for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
+ if (nai.networkCapabilities.getAllowedUidsNoCopy().contains(uid)) {
+ final NetworkCapabilities nc = new NetworkCapabilities(nai.networkCapabilities);
+ NetworkAgentInfo.restrictCapabilitiesFromNetworkAgent(
+ nc,
+ uid,
+ false /* hasAutomotiveFeature (irrelevant) */,
+ mDeps,
+ mCarrierPrivilegeAuthenticator);
+ updateCapabilities(nai.getScore(), nai, nc);
+ }
+ }
+ }
+
/**
* Update the NetworkCapabilities for {@code nai} to {@code nc}. Specifically:
*
@@ -9499,7 +9633,6 @@
final ArraySet<Integer> toAdd = new ArraySet<>(newUids);
toRemove.removeAll(newUids);
toAdd.removeAll(prevUids);
-
try {
if (!toAdd.isEmpty()) {
mNetd.networkAddUidRangesParcel(new NativeUidRangeConfig(
@@ -12673,16 +12806,27 @@
@VisibleForTesting
@NonNull
- ArraySet<NetworkRequestInfo> createNrisFromMobileDataPreferredUids(
- @NonNull final Set<Integer> uids) {
+ ArraySet<NetworkRequestInfo> createNrisForPreferenceOrder(@NonNull final Set<Integer> uids,
+ @NonNull final List<NetworkRequest> requests,
+ final int preferenceOrder) {
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 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, preferenceOrder));
+ return nris;
+ }
+
+ ArraySet<NetworkRequestInfo> createNrisFromMobileDataPreferredUids(
+ @NonNull final Set<Integer> uids) {
final List<NetworkRequest> requests = new ArrayList<>();
// The NRI should be comprised of two layers:
// - The request for the mobile network preferred.
@@ -12691,14 +12835,28 @@
TRANSPORT_CELLULAR, NetworkRequest.Type.REQUEST));
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,
- PREFERENCE_ORDER_MOBILE_DATA_PREFERERRED));
- return nris;
+ return createNrisForPreferenceOrder(uids, requests, PREFERENCE_ORDER_MOBILE_DATA_PREFERERRED
+ );
+ }
+
+ ArraySet<NetworkRequestInfo> createMultiLayerNrisFromSatelliteNetworkFallbackUids(
+ @NonNull final Set<Integer> uids) {
+ final List<NetworkRequest> requests = new ArrayList<>();
+
+ // request: track default(unrestricted internet network)
+ requests.add(createDefaultInternetRequestForTransport(
+ TYPE_NONE, NetworkRequest.Type.TRACK_DEFAULT));
+
+ // request: restricted Satellite internet
+ final NetworkCapabilities cap = new NetworkCapabilities.Builder()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .addTransportType(NetworkCapabilities.TRANSPORT_SATELLITE)
+ .build();
+ requests.add(createNetworkRequest(NetworkRequest.Type.REQUEST, cap));
+
+ return createNrisForPreferenceOrder(uids, requests, PREFERENCE_ORDER_SATELLITE_FALLBACK);
}
private void handleMobileDataPreferredUidsChanged() {
@@ -12710,6 +12868,16 @@
rematchAllNetworksAndRequests();
}
+ private void handleSetSatelliteNetworkPreference(
+ @NonNull final Set<Integer> satelliteNetworkPreferredUids) {
+ removeDefaultNetworkRequestsForPreference(PREFERENCE_ORDER_SATELLITE_FALLBACK);
+ addPerAppDefaultNetworkRequests(
+ createMultiLayerNrisFromSatelliteNetworkFallbackUids(satelliteNetworkPreferredUids)
+ );
+ // Finally, rematch.
+ rematchAllNetworksAndRequests();
+ }
+
private void handleIngressRateLimitChanged() {
final long oldIngressRateLimit = mIngressRateLimit;
mIngressRateLimit = ConnectivitySettingsManager.getIngressRateLimitInBytesPerSecond(
diff --git a/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java b/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java
index 5705ebe..533278e 100644
--- a/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java
+++ b/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java
@@ -79,11 +79,16 @@
@NonNull
private final List<PrivilegeListener> mCarrierPrivilegesChangedListeners = new ArrayList<>();
private final boolean mUseCallbacksForServiceChanged;
+ private final boolean mRequestRestrictedWifiEnabled;
+ @NonNull
+ private final CarrierPrivilegesLostListener mListener;
public CarrierPrivilegeAuthenticator(@NonNull final Context c,
@NonNull final Dependencies deps,
@NonNull final TelephonyManager t,
- @NonNull final TelephonyManagerShim telephonyManagerShim) {
+ @NonNull final TelephonyManagerShim telephonyManagerShim,
+ final boolean requestRestrictedWifiEnabled,
+ @NonNull CarrierPrivilegesLostListener listener) {
mContext = c;
mTelephonyManager = t;
mTelephonyManagerShim = telephonyManagerShim;
@@ -92,6 +97,8 @@
mHandler = new Handler(thread.getLooper());
mUseCallbacksForServiceChanged = deps.isFeatureEnabled(
c, CARRIER_SERVICE_CHANGED_USE_CALLBACK);
+ mRequestRestrictedWifiEnabled = requestRestrictedWifiEnabled;
+ mListener = listener;
final IntentFilter filter = new IntentFilter();
filter.addAction(TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED);
synchronized (mLock) {
@@ -113,8 +120,10 @@
}
public CarrierPrivilegeAuthenticator(@NonNull final Context c,
- @NonNull final TelephonyManager t) {
- this(c, new Dependencies(), t, TelephonyManagerShimImpl.newInstance(t));
+ @NonNull final TelephonyManager t, final boolean requestRestrictedWifiEnabled,
+ @NonNull CarrierPrivilegesLostListener listener) {
+ this(c, new Dependencies(), t, TelephonyManagerShimImpl.newInstance(t),
+ requestRestrictedWifiEnabled, listener);
}
public static class Dependencies {
@@ -133,6 +142,18 @@
}
}
+ /**
+ * Listener interface to get a notification when the carrier App lost its privileges.
+ */
+ public interface CarrierPrivilegesLostListener {
+ /**
+ * Called when the carrier App lost its privileges.
+ *
+ * @param uid The uid of the carrier app which has lost its privileges.
+ */
+ void onCarrierPrivilegesLost(int uid);
+ }
+
private void simConfigChanged() {
synchronized (mLock) {
unregisterCarrierPrivilegesListeners();
@@ -171,7 +192,11 @@
return;
}
synchronized (mLock) {
+ int oldUid = mCarrierServiceUid.get(mLogicalSlot);
mCarrierServiceUid.put(mLogicalSlot, carrierServiceUid);
+ if (oldUid != 0 && oldUid != carrierServiceUid) {
+ mListener.onCarrierPrivilegesLost(oldUid);
+ }
}
}
}
@@ -193,7 +218,11 @@
private void unregisterCarrierPrivilegesListeners() {
for (PrivilegeListener carrierPrivilegesListener : mCarrierPrivilegesChangedListeners) {
removeCarrierPrivilegesListener(carrierPrivilegesListener);
+ int oldUid = mCarrierServiceUid.get(carrierPrivilegesListener.mLogicalSlot);
mCarrierServiceUid.delete(carrierPrivilegesListener.mLogicalSlot);
+ if (oldUid != 0) {
+ mListener.onCarrierPrivilegesLost(oldUid);
+ }
}
mCarrierPrivilegesChangedListeners.clear();
}
@@ -231,7 +260,7 @@
public boolean isCarrierServiceUidForNetworkCapabilities(int callingUid,
@NonNull NetworkCapabilities networkCapabilities) {
if (callingUid == Process.INVALID_UID) return false;
- final int subId;
+ int subId;
if (networkCapabilities.hasSingleTransportBesidesTest(TRANSPORT_CELLULAR)) {
subId = getSubIdFromTelephonySpecifier(networkCapabilities.getNetworkSpecifier());
} else if (networkCapabilities.hasSingleTransportBesidesTest(TRANSPORT_WIFI)) {
@@ -239,6 +268,12 @@
} else {
subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ && mRequestRestrictedWifiEnabled
+ && networkCapabilities.getSubscriptionIds().size() == 1) {
+ subId = networkCapabilities.getSubscriptionIds().toArray(new Integer[0])[0];
+ }
+
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
&& !networkCapabilities.getSubscriptionIds().contains(subId)) {
// Ideally, the code above should just use networkCapabilities.getSubscriptionIds()
@@ -257,10 +292,16 @@
@VisibleForTesting
void updateCarrierServiceUid() {
synchronized (mLock) {
+ SparseIntArray oldCarrierServiceUid = mCarrierServiceUid.clone();
mCarrierServiceUid.clear();
for (int i = 0; i < mModemCount; i++) {
mCarrierServiceUid.put(i, getCarrierServicePackageUidForSlot(i));
}
+ for (int i = 0; i < oldCarrierServiceUid.size(); i++) {
+ if (mCarrierServiceUid.indexOfValue(oldCarrierServiceUid.valueAt(i)) < 0) {
+ mListener.onCarrierPrivilegesLost(oldCarrierServiceUid.valueAt(i));
+ }
+ }
}
}
@@ -340,6 +381,7 @@
public void dump(IndentingPrintWriter pw) {
pw.println("CarrierPrivilegeAuthenticator:");
+ pw.println("mRequestRestrictedWifiEnabled = " + mRequestRestrictedWifiEnabled);
synchronized (mLock) {
final int size = mCarrierServiceUid.size();
for (int i = 0; i < size; ++i) {
diff --git a/service/src/com/android/server/connectivity/ConnectivityFlags.java b/service/src/com/android/server/connectivity/ConnectivityFlags.java
index f8f76ef..bf09160 100644
--- a/service/src/com/android/server/connectivity/ConnectivityFlags.java
+++ b/service/src/com/android/server/connectivity/ConnectivityFlags.java
@@ -36,6 +36,8 @@
public static final String CARRIER_SERVICE_CHANGED_USE_CALLBACK =
"carrier_service_changed_use_callback_version";
+ public static final String REQUEST_RESTRICTED_WIFI =
+ "request_restricted_wifi";
private boolean mNoRematchAllRequestsOnRegister;
/**
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index 50cad45..76993a6 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -1551,7 +1551,7 @@
* @param hasAutomotiveFeature true if this device has the automotive feature, false otherwise
* @param authenticator the carrier privilege authenticator to check for telephony constraints
*/
- public void restrictCapabilitiesFromNetworkAgent(@NonNull final NetworkCapabilities nc,
+ public static void restrictCapabilitiesFromNetworkAgent(@NonNull final NetworkCapabilities nc,
final int creatorUid, final boolean hasAutomotiveFeature,
@NonNull final ConnectivityService.Dependencies deps,
@Nullable final CarrierPrivilegeAuthenticator authenticator) {
@@ -1564,7 +1564,7 @@
}
}
- private boolean areAllowedUidsAcceptableFromNetworkAgent(
+ private static boolean areAllowedUidsAcceptableFromNetworkAgent(
@NonNull final NetworkCapabilities nc, final boolean hasAutomotiveFeature,
@NonNull final ConnectivityService.Dependencies deps,
@Nullable final CarrierPrivilegeAuthenticator carrierPrivilegeAuthenticator) {
diff --git a/service/src/com/android/server/connectivity/SatelliteAccessController.java b/service/src/com/android/server/connectivity/SatelliteAccessController.java
new file mode 100644
index 0000000..0968aff
--- /dev/null
+++ b/service/src/com/android/server/connectivity/SatelliteAccessController.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.app.role.OnRoleHoldersChangedListener;
+import android.app.role.RoleManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Tracks the uid of all the default messaging application which are role_sms role and
+ * satellite_communication permission complaint and requests ConnectivityService to create multi
+ * layer request with satellite internet access support for the default message application.
+ * @hide
+ */
+public class SatelliteAccessController {
+ private static final String TAG = SatelliteAccessController.class.getSimpleName();
+ private final PackageManager mPackageManager;
+ private final Dependencies mDeps;
+ private final DefaultMessageRoleListener mDefaultMessageRoleListener;
+ private final Consumer<Set<Integer>> mCallback;
+ private final Set<Integer> mSatelliteNetworkPreferredUidCache = new ArraySet<>();
+ private final Handler mConnectivityServiceHandler;
+
+ /**
+ * Monitor {@link android.app.role.OnRoleHoldersChangedListener#onRoleHoldersChanged(String,
+ * UserHandle)},
+ *
+ */
+ private final class DefaultMessageRoleListener
+ implements OnRoleHoldersChangedListener {
+ @Override
+ public void onRoleHoldersChanged(String role, UserHandle user) {
+ if (RoleManager.ROLE_SMS.equals(role)) {
+ Log.i(TAG, "ROLE_SMS Change detected ");
+ onRoleSmsChanged();
+ }
+ }
+
+ public void register() {
+ try {
+ mDeps.addOnRoleHoldersChangedListenerAsUser(
+ mConnectivityServiceHandler::post, this, UserHandle.ALL);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Could not register satellite controller listener due to " + e);
+ }
+ }
+ }
+
+ public SatelliteAccessController(@NonNull final Context c,
+ Consumer<Set<Integer>> callback,
+ @NonNull final Handler connectivityServiceInternalHandler) {
+ this(c, new Dependencies(c), callback, connectivityServiceInternalHandler);
+ }
+
+ public static class Dependencies {
+ private final RoleManager mRoleManager;
+
+ private Dependencies(Context context) {
+ mRoleManager = context.getSystemService(RoleManager.class);
+ }
+
+ /** See {@link RoleManager#getRoleHolders(String)} */
+ public List<String> getRoleHolders(String roleName) {
+ return mRoleManager.getRoleHolders(roleName);
+ }
+
+ /** See {@link RoleManager#addOnRoleHoldersChangedListenerAsUser} */
+ public void addOnRoleHoldersChangedListenerAsUser(@NonNull Executor executor,
+ @NonNull OnRoleHoldersChangedListener listener, UserHandle user) {
+ mRoleManager.addOnRoleHoldersChangedListenerAsUser(executor, listener, user);
+ }
+ }
+
+ @VisibleForTesting
+ SatelliteAccessController(@NonNull final Context c, @NonNull final Dependencies deps,
+ Consumer<Set<Integer>> callback,
+ @NonNull final Handler connectivityServiceInternalHandler) {
+ mDeps = deps;
+ mPackageManager = c.getPackageManager();
+ mDefaultMessageRoleListener = new DefaultMessageRoleListener();
+ mCallback = callback;
+ mConnectivityServiceHandler = connectivityServiceInternalHandler;
+ }
+
+ private void updateSatelliteNetworkPreferredUidListCache(List<String> packageNames) {
+ for (String packageName : packageNames) {
+ // Check if SATELLITE_COMMUNICATION permission is enabled for default sms application
+ // package before adding it part of satellite network preferred uid cache list.
+ if (isSatellitePermissionEnabled(packageName)) {
+ mSatelliteNetworkPreferredUidCache.add(getUidForPackage(packageName));
+ }
+ }
+ }
+
+ //Check if satellite communication is enabled for the package
+ private boolean isSatellitePermissionEnabled(String packageName) {
+ if (mPackageManager != null) {
+ return mPackageManager.checkPermission(
+ Manifest.permission.SATELLITE_COMMUNICATION, packageName)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+ return false;
+ }
+
+ private int getUidForPackage(String pkgName) {
+ if (pkgName == null) {
+ return Process.INVALID_UID;
+ }
+ try {
+ if (mPackageManager != null) {
+ ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(pkgName, 0);
+ if (applicationInfo != null) {
+ return applicationInfo.uid;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException exception) {
+ Log.e(TAG, "Unable to find uid for package: " + pkgName);
+ }
+ return Process.INVALID_UID;
+ }
+
+ //on Role sms change triggered by OnRoleHoldersChangedListener()
+ private void onRoleSmsChanged() {
+ final List<String> packageNames = getRoleSmsChangedPackageName();
+
+ // Create a new Set
+ Set<Integer> previousSatellitePreferredUid = new ArraySet<>(
+ mSatelliteNetworkPreferredUidCache);
+
+ mSatelliteNetworkPreferredUidCache.clear();
+
+ if (packageNames != null) {
+ Log.i(TAG, "role_sms_packages: " + packageNames);
+ // On Role change listener, update the satellite network preferred uid cache list
+ updateSatelliteNetworkPreferredUidListCache(packageNames);
+ Log.i(TAG, "satellite_preferred_uid: " + mSatelliteNetworkPreferredUidCache);
+ } else {
+ Log.wtf(TAG, "package name was found null");
+ }
+
+ // on Role change, update the multilayer request at ConnectivityService with updated
+ // satellite network preferred uid cache list if changed or to revoke for previous default
+ // sms app
+ if (!mSatelliteNetworkPreferredUidCache.equals(previousSatellitePreferredUid)) {
+ Log.i(TAG, "update multi layer request");
+ mCallback.accept(mSatelliteNetworkPreferredUidCache);
+ }
+ }
+
+ private List<String> getRoleSmsChangedPackageName() {
+ try {
+ return mDeps.getRoleHolders(RoleManager.ROLE_SMS);
+ } catch (RuntimeException e) {
+ Log.wtf(TAG, "Could not get package name at role sms change update due to: " + e);
+ return null;
+ }
+ }
+
+ /** Register OnRoleHoldersChangedListener */
+ public void start() {
+ mConnectivityServiceHandler.post(this::onRoleSmsChanged);
+ mDefaultMessageRoleListener.register();
+ }
+}
diff --git a/tests/cts/net/src/android/net/cts/OffloadServiceInfoTest.kt b/tests/cts/net/src/android/net/cts/OffloadServiceInfoTest.kt
new file mode 100644
index 0000000..36de4f2
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/OffloadServiceInfoTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 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.cts
+
+import android.net.nsd.OffloadEngine.OFFLOAD_TYPE_FILTER_QUERIES
+import android.net.nsd.OffloadServiceInfo
+import android.os.Build
+import androidx.test.filters.SmallTest
+import com.android.testutils.ConnectivityModuleTest
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import kotlin.test.assertContentEquals
+import kotlin.test.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** CTS tests for {@link OffloadServiceInfo}. */
+@IgnoreUpTo(Build.VERSION_CODES.S_V2)
+@RunWith(DevSdkIgnoreRunner::class)
+@SmallTest
+@ConnectivityModuleTest
+class OffloadServiceInfoTest {
+ @Test
+ fun testCreateOffloadServiceInfo() {
+ val offloadServiceInfo = OffloadServiceInfo(
+ OffloadServiceInfo.Key("_testService", "_testType"),
+ listOf("_sub1", "_sub2"),
+ "Android.local",
+ byteArrayOf(0x1, 0x2, 0x3),
+ 1 /* priority */,
+ OFFLOAD_TYPE_FILTER_QUERIES.toLong()
+ )
+
+ assertEquals(OffloadServiceInfo.Key("_testService", "_testType"), offloadServiceInfo.key)
+ assertEquals(listOf("_sub1", "_sub2"), offloadServiceInfo.subtypes)
+ assertEquals("Android.local", offloadServiceInfo.hostname)
+ assertContentEquals(byteArrayOf(0x1, 0x2, 0x3), offloadServiceInfo.offloadPayload)
+ assertEquals(1, offloadServiceInfo.priority)
+ assertEquals(OFFLOAD_TYPE_FILTER_QUERIES.toLong(), offloadServiceInfo.offloadType)
+ }
+}
diff --git a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 9b1bf6e..9148770 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -56,10 +56,12 @@
import com.android.server.NetworkAgentWrapper
import com.android.server.TestNetIdManager
import com.android.server.connectivity.CarrierPrivilegeAuthenticator
+import com.android.server.connectivity.CarrierPrivilegeAuthenticator.CarrierPrivilegesLostListener
import com.android.server.connectivity.ConnectivityResources
import com.android.server.connectivity.MockableSystemProperties
import com.android.server.connectivity.MultinetworkPolicyTracker
import com.android.server.connectivity.ProxyTracker
+import com.android.server.connectivity.SatelliteAccessController
import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.DeviceInfoUtils
import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
@@ -86,6 +88,7 @@
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
import org.mockito.Spy
+import java.util.function.Consumer
const val SERVICE_BIND_TIMEOUT_MS = 5_000L
const val TEST_TIMEOUT_MS = 10_000L
@@ -240,15 +243,25 @@
override fun makeCarrierPrivilegeAuthenticator(
context: Context,
- tm: TelephonyManager
+ tm: TelephonyManager,
+ requestRestrictedWifiEnabled: Boolean,
+ listener: CarrierPrivilegesLostListener
): CarrierPrivilegeAuthenticator {
return CarrierPrivilegeAuthenticator(context,
object : CarrierPrivilegeAuthenticator.Dependencies() {
override fun makeHandlerThread(): HandlerThread =
super.makeHandlerThread().also { handlerThreads.add(it) }
},
- tm, TelephonyManagerShimImpl.newInstance(tm))
+ tm, TelephonyManagerShimImpl.newInstance(tm),
+ requestRestrictedWifiEnabled, listener)
}
+
+ override fun makeSatelliteAccessController(
+ context: Context,
+ updateSatellitePreferredUid: Consumer<MutableSet<Int>>?,
+ connectivityServiceInternalHandler: Handler
+ ): SatelliteAccessController? = mock(
+ SatelliteAccessController::class.java)
}
@After
diff --git a/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt b/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt
index cb3a315..470274d 100644
--- a/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt
+++ b/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt
@@ -95,11 +95,11 @@
// Check resource with invalid transport type.
assertRunWithException(arrayOf("-1,3"))
- assertRunWithException(arrayOf("10,3"))
+ assertRunWithException(arrayOf("11,3"))
// Check valid customization generates expected array.
val validRes = arrayOf("0,3", "1,0", "4,4")
- val expectedValidRes = intArrayOf(3, 0, 0, 0, 4, 0, 0, 0, 0, 0)
+ val expectedValidRes = intArrayOf(3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0)
val mockContext = getMockedContextWithStringArrayRes(
R.array.config_networkSupportedKeepaliveCount,
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 5562b67..6623bbd 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -157,6 +157,7 @@
import static android.telephony.DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
import static android.telephony.DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+import static com.android.server.ConnectivityService.ALLOW_SATALLITE_NETWORK_FALLBACK;
import static com.android.server.ConnectivityService.DELAY_DESTROY_FROZEN_SOCKETS_VERSION;
import static com.android.net.module.util.DeviceConfigUtils.TETHERING_MODULE_NAME;
import static com.android.server.ConnectivityService.ALLOW_SYSUI_CONNECTIVITY_REPORTS;
@@ -172,6 +173,7 @@
import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType;
import static com.android.server.NetworkAgentWrapper.CallbackType.OnQosCallbackRegister;
import static com.android.server.NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister;
+import static com.android.server.connectivity.CarrierPrivilegeAuthenticator.CarrierPrivilegesLostListener;
import static com.android.testutils.Cleanup.testAndCleanup;
import static com.android.testutils.ConcurrentUtils.await;
import static com.android.testutils.ConcurrentUtils.durationOf;
@@ -420,6 +422,7 @@
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.QosCallbackTracker;
+import com.android.server.connectivity.SatelliteAccessController;
import com.android.server.connectivity.TcpKeepaliveController;
import com.android.server.connectivity.UidRangeUtils;
import com.android.server.connectivity.VpnProfileStore;
@@ -641,6 +644,7 @@
@Mock DestroySocketsWrapper mDestroySocketsWrapper;
@Mock SubscriptionManager mSubscriptionManager;
@Mock KeepaliveTracker.Dependencies mMockKeepaliveTrackerDependencies;
+ @Mock SatelliteAccessController mSatelliteAccessController;
// BatteryStatsManager is final and cannot be mocked with regular mockito, so just mock the
// underlying binder calls.
@@ -2053,11 +2057,21 @@
@Override
public CarrierPrivilegeAuthenticator makeCarrierPrivilegeAuthenticator(
@NonNull final Context context,
- @NonNull final TelephonyManager tm) {
+ @NonNull final TelephonyManager tm,
+ final boolean requestRestrictedWifiEnabled,
+ CarrierPrivilegesLostListener listener) {
return mDeps.isAtLeastT() ? mCarrierPrivilegeAuthenticator : null;
}
@Override
+ public SatelliteAccessController makeSatelliteAccessController(
+ @NonNull final Context context,
+ Consumer<Set<Integer>> updateSatelliteNetworkFallbackUidCallback,
+ @NonNull final Handler connectivityServiceInternalHandler) {
+ return mSatelliteAccessController;
+ }
+
+ @Override
public boolean intentFilterEquals(final PendingIntent a, final PendingIntent b) {
return runAsShell(GET_INTENT_SENDER_INTENT, () -> a.intentFilterEquals(b));
}
@@ -2147,6 +2161,8 @@
case ConnectivityFlags.NO_REMATCH_ALL_REQUESTS_ON_REGISTER:
case ConnectivityFlags.CARRIER_SERVICE_CHANGED_USE_CALLBACK:
return true;
+ case ConnectivityFlags.REQUEST_RESTRICTED_WIFI:
+ return true;
case KEY_DESTROY_FROZEN_SOCKETS_VERSION:
return true;
case DELAY_DESTROY_FROZEN_SOCKETS_VERSION:
@@ -2163,6 +2179,8 @@
return true;
case LOG_BPF_RC:
return true;
+ case ALLOW_SATALLITE_NETWORK_FALLBACK:
+ return true;
default:
return super.isFeatureNotChickenedOut(context, name);
}
@@ -17353,6 +17371,14 @@
.build();
}
+ private NetworkRequest getRestrictedRequestForWifiWithSubIds() {
+ return new NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .setSubscriptionIds(Collections.singleton(Process.myUid()))
+ .build();
+ }
+
@Test
public void testNetworkRequestWithSubIdsWithNetworkFactoryPermission() throws Exception {
mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_GRANTED);
@@ -17386,6 +17412,134 @@
}
@Test
+ @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testCarrierConfigAppSendNetworkRequestForRestrictedWifi() throws Exception {
+ mServiceContext.setPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, PERMISSION_DENIED);
+ doReturn(true).when(mCarrierPrivilegeAuthenticator)
+ .isCarrierServiceUidForNetworkCapabilities(anyInt(), any());
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE);
+ final NetworkCallback networkCallback1 = new NetworkCallback();
+ final NetworkCallback networkCallback2 = new NetworkCallback();
+
+ mCm.requestNetwork(getRestrictedRequestForWifiWithSubIds(), networkCallback1);
+ mCm.requestNetwork(getRestrictedRequestForWifiWithSubIds(), pendingIntent);
+ mCm.registerNetworkCallback(getRestrictedRequestForWifiWithSubIds(), networkCallback2);
+
+ mCm.unregisterNetworkCallback(networkCallback1);
+ mCm.releaseNetworkRequest(pendingIntent);
+ mCm.unregisterNetworkCallback(networkCallback2);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testRestrictedRequestRemovedDueToCarrierPrivilegesLost() throws Exception {
+ mServiceContext.setPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, PERMISSION_DENIED);
+ NetworkCapabilities filter = getRestrictedRequestForWifiWithSubIds().networkCapabilities;
+ final HandlerThread handlerThread = new HandlerThread("testRestrictedFactoryRequests");
+ handlerThread.start();
+ final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
+ mServiceContext, "testFactory", filter, mCsHandlerThread);
+ testFactory.register();
+
+ testFactory.assertRequestCountEquals(0);
+ doReturn(true).when(mCarrierPrivilegeAuthenticator)
+ .isCarrierServiceUidForNetworkCapabilities(eq(Process.myUid()), any());
+ final TestNetworkCallback networkCallback1 = new TestNetworkCallback();
+ final NetworkRequest networkrequest1 = getRestrictedRequestForWifiWithSubIds();
+ mCm.requestNetwork(networkrequest1, networkCallback1);
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(1);
+
+ NetworkCapabilities nc = new NetworkCapabilities.Builder(filter)
+ .setAllowedUids(Set.of(Process.myUid()))
+ .build();
+ mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, new LinkProperties(), nc);
+ mWiFiAgent.connect(true);
+ networkCallback1.expectAvailableThenValidatedCallbacks(mWiFiAgent);
+
+ final NetworkAgentInfo nai = mService.getNetworkAgentInfoForNetwork(
+ mWiFiAgent.getNetwork());
+
+ doReturn(false).when(mCarrierPrivilegeAuthenticator)
+ .isCarrierServiceUidForNetworkCapabilities(eq(Process.myUid()), any());
+ final CarrierPrivilegesLostListener carrierPrivilegesLostListener =
+ mService.getCarrierPrivilegesLostListener();
+ carrierPrivilegesLostListener.onCarrierPrivilegesLost(Process.myUid());
+ waitForIdle();
+
+ testFactory.expectRequestRemove();
+ testFactory.assertRequestCountEquals(0);
+ assertTrue(nai.networkCapabilities.getAllowedUidsNoCopy().isEmpty());
+ networkCallback1.expect(NETWORK_CAPS_UPDATED);
+ networkCallback1.expect(UNAVAILABLE);
+
+ handlerThread.quitSafely();
+ handlerThread.join();
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testRequestNotRemoved_MismatchUid() throws Exception {
+ mServiceContext.setPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, PERMISSION_DENIED);
+ NetworkCapabilities filter = getRestrictedRequestForWifiWithSubIds().networkCapabilities;
+ final HandlerThread handlerThread = new HandlerThread("testRestrictedFactoryRequests");
+ handlerThread.start();
+
+ final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
+ mServiceContext, "testFactory", filter, mCsHandlerThread);
+ testFactory.register();
+
+ doReturn(true).when(mCarrierPrivilegeAuthenticator)
+ .isCarrierServiceUidForNetworkCapabilities(anyInt(), any());
+ final TestNetworkCallback networkCallback1 = new TestNetworkCallback();
+ final NetworkRequest networkrequest1 = getRestrictedRequestForWifiWithSubIds();
+ mCm.requestNetwork(networkrequest1, networkCallback1);
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(1);
+
+ doReturn(false).when(mCarrierPrivilegeAuthenticator)
+ .isCarrierServiceUidForNetworkCapabilities(eq(Process.myUid()), any());
+ final CarrierPrivilegesLostListener carrierPrivilegesLostListener =
+ mService.getCarrierPrivilegesLostListener();
+ carrierPrivilegesLostListener.onCarrierPrivilegesLost(Process.myUid() + 1);
+ expectNoRequestChanged(testFactory);
+
+ handlerThread.quitSafely();
+ handlerThread.join();
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testRequestNotRemoved_HasRestrictedNetworkPermission() throws Exception {
+ mServiceContext.setPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, PERMISSION_GRANTED);
+ NetworkCapabilities filter = getRestrictedRequestForWifiWithSubIds().networkCapabilities;
+ final HandlerThread handlerThread = new HandlerThread("testRestrictedFactoryRequests");
+ handlerThread.start();
+
+ final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
+ mServiceContext, "testFactory", filter, mCsHandlerThread);
+ testFactory.register();
+
+ doReturn(true).when(mCarrierPrivilegeAuthenticator)
+ .isCarrierServiceUidForNetworkCapabilities(anyInt(), any());
+ final TestNetworkCallback networkCallback1 = new TestNetworkCallback();
+ final NetworkRequest networkrequest1 = getRestrictedRequestForWifiWithSubIds();
+ mCm.requestNetwork(networkrequest1, networkCallback1);
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(1);
+
+ doReturn(false).when(mCarrierPrivilegeAuthenticator)
+ .isCarrierServiceUidForNetworkCapabilities(eq(Process.myUid()), any());
+ final CarrierPrivilegesLostListener carrierPrivilegesLostListener =
+ mService.getCarrierPrivilegesLostListener();
+ carrierPrivilegesLostListener.onCarrierPrivilegesLost(Process.myUid());
+ expectNoRequestChanged(testFactory);
+
+ handlerThread.quitSafely();
+ handlerThread.join();
+ }
+ @Test
public void testAllowedUids() throws Exception {
final int preferenceOrder =
ConnectivityService.PREFERENCE_ORDER_IRRELEVANT_BECAUSE_NOT_DEFAULT;
diff --git a/tests/unit/java/com/android/server/connectivity/CarrierPrivilegeAuthenticatorTest.java b/tests/unit/java/com/android/server/connectivity/CarrierPrivilegeAuthenticatorTest.java
index f07593e..9f0ec30 100644
--- a/tests/unit/java/com/android/server/connectivity/CarrierPrivilegeAuthenticatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/CarrierPrivilegeAuthenticatorTest.java
@@ -20,6 +20,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
+import static com.android.server.connectivity.CarrierPrivilegeAuthenticator.CarrierPrivilegesLostListener;
import static com.android.server.connectivity.ConnectivityFlags.CARRIER_SERVICE_CHANGED_USE_CALLBACK;
import static org.junit.Assert.assertEquals;
@@ -54,10 +55,12 @@
import com.android.networkstack.apishim.common.TelephonyManagerShim.CarrierPrivilegesListenerShim;
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
import com.android.server.connectivity.CarrierPrivilegeAuthenticator.Dependencies;
+import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.After;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -67,6 +70,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
+import java.util.Set;
/**
* Tests for CarrierPrivilegeAuthenticatorTest.
@@ -77,6 +81,9 @@
@RunWith(DevSdkIgnoreRunner.class)
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
public class CarrierPrivilegeAuthenticatorTest {
+ @Rule
+ public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
private static final int SUBSCRIPTION_COUNT = 2;
private static final int TEST_SUBSCRIPTION_ID = 1;
@@ -85,7 +92,9 @@
@NonNull private final TelephonyManagerShimImpl mTelephonyManagerShim;
@NonNull private final PackageManager mPackageManager;
@NonNull private TestCarrierPrivilegeAuthenticator mCarrierPrivilegeAuthenticator;
+ @NonNull private final CarrierPrivilegesLostListener mListener;
private final int mCarrierConfigPkgUid = 12345;
+ private final boolean mUseCallbacks;
private final String mTestPkg = "com.android.server.connectivity.test";
private final BroadcastReceiver mMultiSimBroadcastReceiver;
@NonNull private final HandlerThread mHandlerThread;
@@ -94,7 +103,8 @@
TestCarrierPrivilegeAuthenticator(@NonNull final Context c,
@NonNull final Dependencies deps,
@NonNull final TelephonyManager t) {
- super(c, deps, t, mTelephonyManagerShim);
+ super(c, deps, t, mTelephonyManagerShim, true /* requestRestrictedWifiEnabled */,
+ mListener);
}
@Override
protected int getSlotIndex(int subId) {
@@ -119,7 +129,9 @@
mTelephonyManager = mock(TelephonyManager.class);
mTelephonyManagerShim = mock(TelephonyManagerShimImpl.class);
mPackageManager = mock(PackageManager.class);
+ mListener = mock(CarrierPrivilegesLostListener.class);
mHandlerThread = new HandlerThread(CarrierPrivilegeAuthenticatorTest.class.getSimpleName());
+ mUseCallbacks = useCallbacks;
final Dependencies deps = mock(Dependencies.class);
doReturn(useCallbacks).when(deps).isFeatureEnabled(any() /* context */,
eq(CARRIER_SERVICE_CHANGED_USE_CALLBACK));
@@ -220,6 +232,18 @@
}
@Test
+ @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testCarrierPrivilegesLostDueToCarrierServiceUpdate() throws Exception {
+ final CarrierPrivilegesListenerShim l = getCarrierPrivilegesListeners().get(0);
+
+ l.onCarrierServiceChanged(null, mCarrierConfigPkgUid);
+ l.onCarrierServiceChanged(null, mCarrierConfigPkgUid + 1);
+ if (mUseCallbacks) {
+ verify(mListener).onCarrierPrivilegesLost(eq(mCarrierConfigPkgUid));
+ }
+ }
+
+ @Test
public void testOnCarrierPrivilegesChanged() throws Exception {
final CarrierPrivilegesListenerShim listener = getCarrierPrivilegesListeners().get(0);
@@ -264,4 +288,32 @@
assertFalse(mCarrierPrivilegeAuthenticator.isCarrierServiceUidForNetworkCapabilities(
mCarrierConfigPkgUid, ncBuilder.build()));
}
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testNetworkCapabilitiesContainOneSubId() throws Exception {
+ final CarrierPrivilegesListenerShim listener = getCarrierPrivilegesListeners().get(0);
+ listener.onCarrierServiceChanged(null, mCarrierConfigPkgUid);
+
+ final NetworkCapabilities.Builder ncBuilder = new NetworkCapabilities.Builder();
+ ncBuilder.addTransportType(TRANSPORT_WIFI);
+ ncBuilder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ ncBuilder.setSubscriptionIds(Set.of(0));
+ assertTrue(mCarrierPrivilegeAuthenticator.isCarrierServiceUidForNetworkCapabilities(
+ mCarrierConfigPkgUid, ncBuilder.build()));
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testNetworkCapabilitiesContainTwoSubIds() throws Exception {
+ final CarrierPrivilegesListenerShim listener = getCarrierPrivilegesListeners().get(0);
+ listener.onCarrierServiceChanged(null, mCarrierConfigPkgUid);
+
+ final NetworkCapabilities.Builder ncBuilder = new NetworkCapabilities.Builder();
+ ncBuilder.addTransportType(TRANSPORT_WIFI);
+ ncBuilder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ ncBuilder.setSubscriptionIds(Set.of(0, 1));
+ assertFalse(mCarrierPrivilegeAuthenticator.isCarrierServiceUidForNetworkCapabilities(
+ mCarrierConfigPkgUid, ncBuilder.build()));
+ }
}
diff --git a/tests/unit/java/com/android/server/connectivity/SatelliteAccessControllerTest.kt b/tests/unit/java/com/android/server/connectivity/SatelliteAccessControllerTest.kt
new file mode 100644
index 0000000..64a515a
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/SatelliteAccessControllerTest.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.connectivity
+
+import android.Manifest
+import android.app.role.OnRoleHoldersChangedListener
+import android.app.role.RoleManager
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Handler
+import android.os.UserHandle
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import java.util.concurrent.Executor
+import java.util.function.Consumer
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+private const val DEFAULT_MESSAGING_APP1 = "default_messaging_app_1"
+private const val DEFAULT_MESSAGING_APP2 = "default_messaging_app_2"
+private const val DEFAULT_MESSAGING_APP1_UID = 1234
+private const val DEFAULT_MESSAGING_APP2_UID = 5678
+
+@RunWith(DevSdkIgnoreRunner::class)
+@IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+class SatelliteAccessControllerTest {
+ private val context = mock(Context::class.java)
+ private val mPackageManager = mock(PackageManager::class.java)
+ private val mHandler = mock(Handler::class.java)
+ private val mRoleManager =
+ mock(SatelliteAccessController.Dependencies::class.java)
+ private val mCallback = mock(Consumer::class.java) as Consumer<Set<Int>>
+ private val mSatelliteAccessController by lazy {
+ SatelliteAccessController(context, mRoleManager, mCallback, mHandler)}
+ private var mRoleHolderChangedListener: OnRoleHoldersChangedListener? = null
+ @Before
+ @Throws(PackageManager.NameNotFoundException::class)
+ fun setup() {
+ doReturn(mPackageManager).`when`(context).packageManager
+ doReturn(PackageManager.PERMISSION_GRANTED)
+ .`when`(mPackageManager)
+ .checkPermission(Manifest.permission.SATELLITE_COMMUNICATION, DEFAULT_MESSAGING_APP1)
+ doReturn(PackageManager.PERMISSION_GRANTED)
+ .`when`(mPackageManager)
+ .checkPermission(Manifest.permission.SATELLITE_COMMUNICATION, DEFAULT_MESSAGING_APP2)
+
+ // Initialise default message application package1
+ val applicationInfo1 = ApplicationInfo()
+ applicationInfo1.uid = DEFAULT_MESSAGING_APP1_UID
+ doReturn(applicationInfo1)
+ .`when`(mPackageManager)
+ .getApplicationInfo(eq(DEFAULT_MESSAGING_APP1), anyInt())
+
+ // Initialise default message application package2
+ val applicationInfo2 = ApplicationInfo()
+ applicationInfo2.uid = DEFAULT_MESSAGING_APP2_UID
+ doReturn(applicationInfo2)
+ .`when`(mPackageManager)
+ .getApplicationInfo(eq(DEFAULT_MESSAGING_APP2), anyInt())
+
+ // Get registered listener using captor
+ val listenerCaptor = ArgumentCaptor.forClass(
+ OnRoleHoldersChangedListener::class.java
+ )
+ mSatelliteAccessController.start()
+ verify(mRoleManager).addOnRoleHoldersChangedListenerAsUser(
+ any(Executor::class.java), listenerCaptor.capture(), any(UserHandle::class.java))
+ mRoleHolderChangedListener = listenerCaptor.value
+ }
+
+ @Test
+ fun test_onRoleHoldersChanged_SatellitePreferredUid_Changed() {
+ doReturn(listOf<String>()).`when`(mRoleManager).getRoleHolders(RoleManager.ROLE_SMS)
+ val satelliteNetworkPreferredSet =
+ ArgumentCaptor.forClass(Set::class.java) as ArgumentCaptor<Set<Int>>
+ mRoleHolderChangedListener?.onRoleHoldersChanged(RoleManager.ROLE_SMS, UserHandle.ALL)
+ verify(mCallback, never()).accept(satelliteNetworkPreferredSet.capture())
+
+ // check DEFAULT_MESSAGING_APP1 is available as satellite network preferred uid
+ doReturn(listOf(DEFAULT_MESSAGING_APP1))
+ .`when`(mRoleManager).getRoleHolders(RoleManager.ROLE_SMS)
+ mRoleHolderChangedListener?.onRoleHoldersChanged(RoleManager.ROLE_SMS, UserHandle.ALL)
+ verify(mCallback).accept(satelliteNetworkPreferredSet.capture())
+ var satelliteNetworkPreferredUids = satelliteNetworkPreferredSet.value
+ assertEquals(1, satelliteNetworkPreferredUids.size)
+ assertTrue(satelliteNetworkPreferredUids.contains(DEFAULT_MESSAGING_APP1_UID))
+ assertFalse(satelliteNetworkPreferredUids.contains(DEFAULT_MESSAGING_APP2_UID))
+
+ // check DEFAULT_MESSAGING_APP1 and DEFAULT_MESSAGING_APP2 is available
+ // as satellite network preferred uid
+ val dmas: MutableList<String> = ArrayList()
+ dmas.add(DEFAULT_MESSAGING_APP1)
+ dmas.add(DEFAULT_MESSAGING_APP2)
+ doReturn(dmas).`when`(mRoleManager).getRoleHolders(RoleManager.ROLE_SMS)
+ mRoleHolderChangedListener?.onRoleHoldersChanged(RoleManager.ROLE_SMS, UserHandle.ALL)
+ verify(mCallback, times(2))
+ .accept(satelliteNetworkPreferredSet.capture())
+ satelliteNetworkPreferredUids = satelliteNetworkPreferredSet.value
+ assertEquals(2, satelliteNetworkPreferredUids.size)
+ assertTrue(satelliteNetworkPreferredUids.contains(DEFAULT_MESSAGING_APP1_UID))
+ assertTrue(satelliteNetworkPreferredUids.contains(DEFAULT_MESSAGING_APP2_UID))
+
+ // check no uid is available as satellite network preferred uid
+ doReturn(listOf<String>()).`when`(mRoleManager).getRoleHolders(RoleManager.ROLE_SMS)
+ mRoleHolderChangedListener?.onRoleHoldersChanged(RoleManager.ROLE_SMS, UserHandle.ALL)
+ verify(mCallback, times(3))
+ .accept(satelliteNetworkPreferredSet.capture())
+ satelliteNetworkPreferredUids = satelliteNetworkPreferredSet.value
+ assertEquals(0, satelliteNetworkPreferredUids.size)
+ assertFalse(satelliteNetworkPreferredUids.contains(DEFAULT_MESSAGING_APP1_UID))
+ assertFalse(satelliteNetworkPreferredUids.contains(DEFAULT_MESSAGING_APP2_UID))
+
+ // No Change received at OnRoleSmsChanged, check callback not triggered
+ doReturn(listOf<String>()).`when`(mRoleManager).getRoleHolders(RoleManager.ROLE_SMS)
+ mRoleHolderChangedListener?.onRoleHoldersChanged(RoleManager.ROLE_SMS, UserHandle.ALL)
+ verify(mCallback, times(3))
+ .accept(satelliteNetworkPreferredSet.capture())
+ }
+
+ @Test
+ fun test_onRoleHoldersChanged_NoSatelliteCommunicationPermission() {
+ doReturn(listOf<Any>()).`when`(mRoleManager).getRoleHolders(RoleManager.ROLE_SMS)
+ val satelliteNetworkPreferredSet =
+ ArgumentCaptor.forClass(Set::class.java) as ArgumentCaptor<Set<Int>>
+ mRoleHolderChangedListener?.onRoleHoldersChanged(RoleManager.ROLE_SMS, UserHandle.ALL)
+ verify(mCallback, never()).accept(satelliteNetworkPreferredSet.capture())
+
+ // check DEFAULT_MESSAGING_APP1 is not available as satellite network preferred uid
+ // since satellite communication permission not available.
+ doReturn(PackageManager.PERMISSION_DENIED)
+ .`when`(mPackageManager)
+ .checkPermission(Manifest.permission.SATELLITE_COMMUNICATION, DEFAULT_MESSAGING_APP1)
+ doReturn(listOf(DEFAULT_MESSAGING_APP1))
+ .`when`(mRoleManager).getRoleHolders(RoleManager.ROLE_SMS)
+ mRoleHolderChangedListener?.onRoleHoldersChanged(RoleManager.ROLE_SMS, UserHandle.ALL)
+ verify(mCallback, never()).accept(satelliteNetworkPreferredSet.capture())
+ }
+
+ @Test
+ fun test_onRoleHoldersChanged_RoleSms_NotAvailable() {
+ doReturn(listOf(DEFAULT_MESSAGING_APP1))
+ .`when`(mRoleManager).getRoleHolders(RoleManager.ROLE_SMS)
+ val satelliteNetworkPreferredSet =
+ ArgumentCaptor.forClass(Set::class.java) as ArgumentCaptor<Set<Int>>
+ mRoleHolderChangedListener?.onRoleHoldersChanged(RoleManager.ROLE_BROWSER, UserHandle.ALL)
+ verify(mCallback, never()).accept(satelliteNetworkPreferredSet.capture())
+ }
+}
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSSatelliteNetworkFallbackTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSSatelliteNetworkFallbackTest.kt
new file mode 100644
index 0000000..9024641
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivityservice/CSSatelliteNetworkFallbackTest.kt
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server
+
+import android.net.IpPrefix
+import android.net.INetd
+import android.net.LinkAddress
+import android.net.LinkProperties
+import android.net.NativeNetworkConfig
+import android.net.NativeNetworkType
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED
+import android.net.NetworkScore
+import android.net.NetworkCapabilities.TRANSPORT_SATELLITE
+import android.net.NetworkScore.KEEP_CONNECTED_FOR_TEST
+import android.net.RouteInfo
+import android.net.UidRange
+import android.net.UidRangeParcel
+import android.net.VpnManager
+import android.net.netd.aidl.NativeUidRangeConfig
+import android.os.Build
+import android.os.UserHandle
+import android.util.ArraySet
+import com.android.net.module.util.CollectionUtils
+import com.android.server.ConnectivityService.PREFERENCE_ORDER_SATELLITE_FALLBACK
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.visibleOnHandlerThread
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.never
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+private const val SECONDARY_USER = 10
+private val SECONDARY_USER_HANDLE = UserHandle(SECONDARY_USER)
+private const val TEST_PACKAGE_UID = 123
+private const val TEST_PACKAGE_UID2 = 321
+
+@DevSdkIgnoreRunner.MonitorThreadLeak
+@RunWith(DevSdkIgnoreRunner::class)
+@IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+class CSSatelliteNetworkPreferredTest : CSTest() {
+ /**
+ * Test createMultiLayerNrisFromSatelliteNetworkPreferredUids returns correct
+ * NetworkRequestInfo.
+ */
+ @Test
+ fun testCreateMultiLayerNrisFromSatelliteNetworkPreferredUids() {
+ // Verify that empty uid set should not create any NRI for it.
+ val nrisNoUid = service.createMultiLayerNrisFromSatelliteNetworkFallbackUids(emptySet())
+ Assert.assertEquals(0, nrisNoUid.size.toLong())
+ val uid1 = PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID)
+ val uid2 = PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID2)
+ val uid3 = SECONDARY_USER_HANDLE.getUid(TEST_PACKAGE_UID)
+ assertCreateMultiLayerNrisFromSatelliteNetworkPreferredUids(mutableSetOf(uid1))
+ assertCreateMultiLayerNrisFromSatelliteNetworkPreferredUids(mutableSetOf(uid1, uid3))
+ assertCreateMultiLayerNrisFromSatelliteNetworkPreferredUids(mutableSetOf(uid1, uid2))
+ }
+
+ /**
+ * Test that SATELLITE_NETWORK_PREFERENCE_UIDS changes will send correct net id and uid ranges
+ * to netd.
+ */
+ @Test
+ fun testSatelliteNetworkPreferredUidsChanged() {
+ val netdInOrder = inOrder(netd)
+
+ val satelliteAgent = createSatelliteAgent("satellite0")
+ satelliteAgent.connect()
+
+ val satelliteNetId = satelliteAgent.network.netId
+ netdInOrder.verify(netd).networkCreate(
+ nativeNetworkConfigPhysical(satelliteNetId, INetd.PERMISSION_NONE))
+
+ val uid1 = PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID)
+ val uid2 = PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID2)
+ val uid3 = SECONDARY_USER_HANDLE.getUid(TEST_PACKAGE_UID)
+
+ // Initial satellite network preferred uids status.
+ setAndUpdateSatelliteNetworkPreferredUids(setOf())
+ netdInOrder.verify(netd, never()).networkAddUidRangesParcel(any())
+ netdInOrder.verify(netd, never()).networkRemoveUidRangesParcel(any())
+
+ // Set SATELLITE_NETWORK_PREFERENCE_UIDS setting and verify that net id and uid ranges
+ // send to netd
+ var uids = mutableSetOf(uid1, uid2, uid3)
+ val uidRanges1 = toUidRangeStableParcels(uidRangesForUids(uids))
+ val config1 = NativeUidRangeConfig(
+ satelliteNetId, uidRanges1,
+ PREFERENCE_ORDER_SATELLITE_FALLBACK
+ )
+ setAndUpdateSatelliteNetworkPreferredUids(uids)
+ netdInOrder.verify(netd).networkAddUidRangesParcel(config1)
+ netdInOrder.verify(netd, never()).networkRemoveUidRangesParcel(any())
+
+ // Set SATELLITE_NETWORK_PREFERENCE_UIDS setting again and verify that old rules are removed
+ // and new rules are added.
+ uids = mutableSetOf(uid1)
+ val uidRanges2: Array<UidRangeParcel?> = toUidRangeStableParcels(uidRangesForUids(uids))
+ val config2 = NativeUidRangeConfig(
+ satelliteNetId, uidRanges2,
+ PREFERENCE_ORDER_SATELLITE_FALLBACK
+ )
+ setAndUpdateSatelliteNetworkPreferredUids(uids)
+ netdInOrder.verify(netd).networkRemoveUidRangesParcel(config1)
+ netdInOrder.verify(netd).networkAddUidRangesParcel(config2)
+ }
+
+ private fun assertCreateMultiLayerNrisFromSatelliteNetworkPreferredUids(uids: Set<Int>) {
+ val nris: Set<ConnectivityService.NetworkRequestInfo> =
+ service.createMultiLayerNrisFromSatelliteNetworkFallbackUids(uids)
+ val 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.toLong())
+ assertTrue(nri.isMultilayerRequest)
+ assertEquals(nri.uids, uidRangesForUids(uids))
+ assertEquals(PREFERENCE_ORDER_SATELLITE_FALLBACK, nri.mPreferenceOrder)
+ }
+
+ private fun setAndUpdateSatelliteNetworkPreferredUids(uids: Set<Int>) {
+ visibleOnHandlerThread(csHandler) {
+ deps.satelliteNetworkFallbackUidUpdate!!.accept(uids)
+ }
+ }
+
+ private fun nativeNetworkConfigPhysical(netId: Int, permission: Int) =
+ NativeNetworkConfig(netId, NativeNetworkType.PHYSICAL, permission,
+ false /* secure */, VpnManager.TYPE_VPN_NONE, false /* excludeLocalRoutes */)
+
+ private fun createSatelliteAgent(name: String): CSAgentWrapper {
+ return Agent(score = keepScore(), lp = lp(name),
+ nc = nc(TRANSPORT_SATELLITE, NET_CAPABILITY_INTERNET)
+ )
+ }
+
+ private fun toUidRangeStableParcels(ranges: Set<UidRange>): Array<UidRangeParcel?> {
+ val stableRanges = arrayOfNulls<UidRangeParcel>(ranges.size)
+ for ((index, range) in ranges.withIndex()) {
+ stableRanges[index] = UidRangeParcel(range.start, range.stop)
+ }
+ return stableRanges
+ }
+
+ private fun uidRangesForUids(vararg uids: Int): Set<UidRange> {
+ val ranges = ArraySet<UidRange>()
+ for (uid in uids) {
+ ranges.add(UidRange(uid, uid))
+ }
+ return ranges
+ }
+
+ private fun uidRangesForUids(uids: Collection<Int>): Set<UidRange> {
+ return uidRangesForUids(*CollectionUtils.toIntArray(uids))
+ }
+
+ private fun nc(transport: Int, vararg caps: Int) = NetworkCapabilities.Builder().apply {
+ addTransportType(transport)
+ caps.forEach {
+ addCapability(it)
+ }
+ // Useful capabilities for everybody
+ addCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ addCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ addCapability(NET_CAPABILITY_NOT_ROAMING)
+ addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+ }.build()
+
+ private fun lp(iface: String) = LinkProperties().apply {
+ interfaceName = iface
+ addLinkAddress(LinkAddress(LOCAL_IPV4_ADDRESS, 32))
+ addRoute(RouteInfo(IpPrefix("0.0.0.0/0"), null, null))
+ }
+
+ // This allows keeping all the networks connected without having to file individual requests
+ // for them.
+ private fun keepScore() = FromS(
+ NetworkScore.Builder().setKeepConnectedReason(KEEP_CONNECTED_FOR_TEST).build()
+ )
+}
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
index 0708669..5bdd060 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
@@ -59,6 +59,7 @@
import com.android.networkstack.apishim.common.UnsupportedApiLevelException
import com.android.server.connectivity.AutomaticOnOffKeepaliveTracker
import com.android.server.connectivity.CarrierPrivilegeAuthenticator
+import com.android.server.connectivity.CarrierPrivilegeAuthenticator.CarrierPrivilegesLostListener
import com.android.server.connectivity.ClatCoordinator
import com.android.server.connectivity.ConnectivityFlags
import com.android.server.connectivity.MulticastRoutingCoordinatorService
@@ -66,7 +67,7 @@
import com.android.server.connectivity.MultinetworkPolicyTrackerTestDependencies
import com.android.server.connectivity.NetworkRequestStateStatsMetrics
import com.android.server.connectivity.ProxyTracker
-import com.android.server.connectivity.RoutingCoordinatorService
+import com.android.server.connectivity.SatelliteAccessController
import com.android.testutils.visibleOnHandlerThread
import com.android.testutils.waitForIdle
import java.util.concurrent.Executors
@@ -77,6 +78,7 @@
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
+import java.util.function.Consumer
internal const val HANDLER_TIMEOUT_MS = 2_000
internal const val BROADCAST_TIMEOUT_MS = 3_000L
@@ -133,10 +135,12 @@
// permissions using static contexts.
val enabledFeatures = HashMap<String, Boolean>().also {
it[ConnectivityFlags.NO_REMATCH_ALL_REQUESTS_ON_REGISTER] = true
+ it[ConnectivityFlags.REQUEST_RESTRICTED_WIFI] = true
it[ConnectivityService.KEY_DESTROY_FROZEN_SOCKETS_VERSION] = true
it[ConnectivityService.DELAY_DESTROY_FROZEN_SOCKETS_VERSION] = true
it[ConnectivityService.ALLOW_SYSUI_CONNECTIVITY_REPORTS] = true
it[ConnectivityService.LOG_BPF_RC] = true
+ it[ConnectivityService.ALLOW_SATALLITE_NETWORK_FALLBACK] = true
}
fun enableFeature(f: String) = enabledFeatures.set(f, true)
fun disableFeature(f: String) = enabledFeatures.set(f, false)
@@ -173,6 +177,7 @@
}
val multicastRoutingCoordinatorService = mock<MulticastRoutingCoordinatorService>()
+ val satelliteAccessController = mock<SatelliteAccessController>()
val deps = CSDeps()
val service = makeConnectivityService(context, netd, deps).also { it.systemReadyInternal() }
@@ -200,9 +205,21 @@
override fun makeCarrierPrivilegeAuthenticator(
context: Context,
- tm: TelephonyManager
+ tm: TelephonyManager,
+ requestRestrictedWifiEnabled: Boolean,
+ listener: CarrierPrivilegesLostListener
) = if (SdkLevel.isAtLeastT()) mock<CarrierPrivilegeAuthenticator>() else null
+ var satelliteNetworkFallbackUidUpdate: Consumer<Set<Int>>? = null
+ override fun makeSatelliteAccessController(
+ context: Context,
+ updateSatelliteNetworkFallackUid: Consumer<Set<Int>>?,
+ csHandlerThread: Handler
+ ): SatelliteAccessController? {
+ satelliteNetworkFallbackUidUpdate = updateSatelliteNetworkFallackUid
+ return satelliteAccessController
+ }
+
private inner class AOOKTDeps(c: Context) : AutomaticOnOffKeepaliveTracker.Dependencies(c) {
override fun isTetheringFeatureNotChickenedOut(name: String): Boolean {
return isFeatureEnabled(context, name)