Add more powerful CTS for allowedUids
This tests that the network providers are subject to appropriate
limitations as to what they can set as allowedUids – both in the
positive (can do) case, and the negative (can't do) case.
Test: this
Change-Id: I115e2a4bc02ddcd03ecf2f35130fcb0378da22bd
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index f959114..efae754 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -1457,6 +1457,18 @@
return mTransportTypes == (1 << transportType);
}
+ /**
+ * Returns true iff this NC has the specified transport and no other, ignoring TRANSPORT_TEST.
+ *
+ * If this NC has the passed transport and no other, this method returns true.
+ * If this NC has the passed transport, TRANSPORT_TEST and no other, this method returns true.
+ * Otherwise, this method returns false.
+ * @hide
+ */
+ public boolean hasSingleTransportBesidesTest(@Transport int transportType) {
+ return (mTransportTypes & ~(1 << TRANSPORT_TEST)) == (1 << transportType);
+ }
+
private boolean satisfiedByTransportTypes(NetworkCapabilities nc) {
return ((this.mTransportTypes == 0)
|| ((this.mTransportTypes & nc.mTransportTypes) != 0));
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index ea6d37e..6a41bc6 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -2649,6 +2649,13 @@
}
}
+ private boolean canSeeAllowedUids(final int pid, final int uid, final int netOwnerUid) {
+ return Process.SYSTEM_UID == uid
+ || netOwnerUid == uid
+ || checkAnyPermissionOf(mContext, pid, uid,
+ android.Manifest.permission.NETWORK_FACTORY);
+ }
+
@VisibleForTesting
NetworkCapabilities networkCapabilitiesRestrictedForCallerPermissions(
NetworkCapabilities nc, int callerPid, int callerUid) {
@@ -2670,8 +2677,7 @@
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)) {
newNc.setAdministratorUids(new int[0]);
}
- if (!checkAnyPermissionOf(mContext,
- callerPid, callerUid, android.Manifest.permission.NETWORK_FACTORY)) {
+ if (!canSeeAllowedUids(callerPid, callerUid, newNc.getOwnerUid())) {
newNc.setAllowedUids(new ArraySet<>());
newNc.setSubscriptionIds(Collections.emptySet());
}
@@ -3944,6 +3950,11 @@
pw.println();
dumpBpfProgramStatus(pw);
+ if (null != mCarrierPrivilegeAuthenticator) {
+ pw.println();
+ mCarrierPrivilegeAuthenticator.dump(pw);
+ }
+
pw.println();
if (!CollectionUtils.contains(args, SHORT_ARG)) {
diff --git a/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java b/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java
index ab7b1a7..8edceb0 100644
--- a/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java
+++ b/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java
@@ -41,6 +41,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.modules.utils.HandlerExecutor;
import com.android.networkstack.apishim.TelephonyManagerShimImpl;
import com.android.networkstack.apishim.common.TelephonyManagerShim;
@@ -125,11 +126,13 @@
private class PrivilegeListener implements CarrierPrivilegesListenerShim {
public final int mLogicalSlot;
+
PrivilegeListener(final int logicalSlot) {
mLogicalSlot = logicalSlot;
}
- @Override public void onCarrierPrivilegesChanged(
+ @Override
+ public void onCarrierPrivilegesChanged(
@NonNull List<String> privilegedPackageNames,
@NonNull int[] privilegedUids) {
if (mUseCallbacksForServiceChanged) return;
@@ -209,7 +212,9 @@
public boolean hasCarrierPrivilegeForNetworkCapabilities(int callingUid,
@NonNull NetworkCapabilities networkCapabilities) {
if (callingUid == Process.INVALID_UID) return false;
- if (!networkCapabilities.hasSingleTransport(TRANSPORT_CELLULAR)) return false;
+ if (!networkCapabilities.hasSingleTransportBesidesTest(TRANSPORT_CELLULAR)) {
+ return false;
+ }
final int subId = getSubIdFromNetworkSpecifier(networkCapabilities.getNetworkSpecifier());
if (SubscriptionManager.INVALID_SUBSCRIPTION_ID == subId) return false;
return callingUid == getCarrierServiceUidForSubId(subId);
@@ -292,4 +297,16 @@
Log.e(TAG, "removeCarrierPrivilegesListener API is not available");
}
}
+
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("CarrierPrivilegeAuthenticator:");
+ synchronized (mLock) {
+ final int size = mCarrierServiceUid.size();
+ for (int i = 0; i < size; ++i) {
+ final int logicalSlot = mCarrierServiceUid.keyAt(i);
+ final int serviceUid = mCarrierServiceUid.valueAt(i);
+ pw.println("Logical slot = " + logicalSlot + " : uid = " + serviceUid);
+ }
+ }
+ }
}
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index 7cd3cc8..567fd41 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -17,6 +17,7 @@
package com.android.server.connectivity;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -1549,7 +1550,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 static void restrictCapabilitiesFromNetworkAgent(@NonNull final NetworkCapabilities nc,
+ public void restrictCapabilitiesFromNetworkAgent(@NonNull final NetworkCapabilities nc,
final int creatorUid, final boolean hasAutomotiveFeature,
@NonNull final ConnectivityService.Dependencies deps,
@Nullable final CarrierPrivilegeAuthenticator authenticator) {
@@ -1562,7 +1563,7 @@
}
}
- private static boolean areAllowedUidsAcceptableFromNetworkAgent(
+ private boolean areAllowedUidsAcceptableFromNetworkAgent(
@NonNull final NetworkCapabilities nc, final boolean hasAutomotiveFeature,
@NonNull final ConnectivityService.Dependencies deps,
@Nullable final CarrierPrivilegeAuthenticator carrierPrivilegeAuthenticator) {
@@ -1575,19 +1576,25 @@
// On a non-restricted network, access UIDs make no sense
if (nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) return false;
- // If this network has TRANSPORT_TEST, then the caller can do whatever they want to
- // access UIDs
- if (nc.hasTransport(TRANSPORT_TEST)) return true;
+ // If this network has TRANSPORT_TEST and nothing else, then the caller can do whatever
+ // they want to access UIDs
+ if (nc.hasSingleTransport(TRANSPORT_TEST)) return true;
- // Factories that make ethernet networks can allow UIDs for automotive devices.
- if (nc.hasSingleTransport(TRANSPORT_ETHERNET) && hasAutomotiveFeature) {
- return true;
+ if (nc.hasTransport(TRANSPORT_ETHERNET)) {
+ // Factories that make ethernet networks can allow UIDs for automotive devices.
+ if (hasAutomotiveFeature) return true;
+ // It's also admissible if the ethernet network has TRANSPORT_TEST, as long as it
+ // doesn't have NET_CAPABILITY_INTERNET so it can't become the default network.
+ if (nc.hasTransport(TRANSPORT_TEST) && !nc.hasCapability(NET_CAPABILITY_INTERNET)) {
+ return true;
+ }
+ return false;
}
// Factories that make cell networks can allow the UID for the carrier service package.
// This can only work in T where there is support for CarrierPrivilegeAuthenticator
if (null != carrierPrivilegeAuthenticator
- && nc.hasSingleTransport(TRANSPORT_CELLULAR)
+ && nc.hasSingleTransportBesidesTest(TRANSPORT_CELLULAR)
&& (1 == nc.getAllowedUidsNoCopy().size())
&& (carrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
nc.getAllowedUidsNoCopy().valueAt(0), nc))) {
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index b86de25..9310888 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -62,10 +62,13 @@
// uncomment when b/13249961 is fixed
// sdk_version: "current",
platform_apis: true,
- data: [":ConnectivityTestPreparer"],
per_testcase_directory: true,
host_required: ["net-tests-utils-host-common"],
test_config_template: "AndroidTestTemplate.xml",
+ data: [
+ ":ConnectivityTestPreparer",
+ ":CtsCarrierServicePackage",
+ ]
}
// Networking CTS tests for development and release. These tests always target the platform SDK
@@ -154,3 +157,17 @@
package_name: "android.net.cts.maxtargetsdk30", // CTS package names must be unique.
instrumentation_target_package: "android.net.cts.maxtargetsdk30",
}
+
+android_test_helper_app {
+ name: "CtsCarrierServicePackage",
+ defaults: ["cts_defaults"],
+ package_name: "android.net.cts.carrierservicepackage",
+ manifest: "carrierservicepackage/AndroidManifest.xml",
+ srcs: ["carrierservicepackage/src/**/*.java"],
+ min_sdk_version: "30",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+}
diff --git a/tests/cts/net/AndroidManifest.xml b/tests/cts/net/AndroidManifest.xml
index 68e36ff..098cc0a 100644
--- a/tests/cts/net/AndroidManifest.xml
+++ b/tests/cts/net/AndroidManifest.xml
@@ -36,6 +36,7 @@
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<!-- This test also uses signature permissions through adopting the shell identity.
The permissions acquired that way include (probably not exhaustive) :
@@ -46,6 +47,7 @@
android:usesCleartextTraffic="true">
<uses-library android:name="android.test.runner" />
<uses-library android:name="org.apache.http.legacy" android:required="false" />
+
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
@@ -54,4 +56,3 @@
</instrumentation>
</manifest>
-
diff --git a/tests/cts/net/AndroidTestTemplate.xml b/tests/cts/net/AndroidTestTemplate.xml
index 8efa99f..38f26d8 100644
--- a/tests/cts/net/AndroidTestTemplate.xml
+++ b/tests/cts/net/AndroidTestTemplate.xml
@@ -27,6 +27,7 @@
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="{MODULE}.apk" />
+ <option name="test-file-name" value="CtsCarrierServicePackage.apk" />
</target_preparer>
<target_preparer class="com.android.testutils.ConnectivityTestTargetPreparer">
</target_preparer>
diff --git a/tests/cts/net/carrierservicepackage/AndroidManifest.xml b/tests/cts/net/carrierservicepackage/AndroidManifest.xml
new file mode 100644
index 0000000..c2a45eb
--- /dev/null
+++ b/tests/cts/net/carrierservicepackage/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ android:versionCode="1"
+ android:versionName="1.0.0"
+ package="android.net.cts.carrierservicepackage">
+ <uses-sdk android:minSdkVersion="30"
+ android:targetSdkVersion="33" />
+ <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+ <application android:allowBackup="false"
+ android:directBootAware="true">
+ <service android:name=".DummyCarrierConfigService"
+ android:permission="android.permission.BIND_CARRIER_SERVICES"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.service.carrier.CarrierService"/>
+ </intent-filter>
+ </service>
+ </application>
+
+</manifest>
diff --git a/tests/cts/net/carrierservicepackage/src/android/net/cts/carrierservicepackage/DummyCarrierConfigService.java b/tests/cts/net/carrierservicepackage/src/android/net/cts/carrierservicepackage/DummyCarrierConfigService.java
new file mode 100644
index 0000000..ca2015b
--- /dev/null
+++ b/tests/cts/net/carrierservicepackage/src/android/net/cts/carrierservicepackage/DummyCarrierConfigService.java
@@ -0,0 +1,45 @@
+/*
+ * 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 android.net.cts.carrierservicepackage;
+
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.PersistableBundle;
+import android.service.carrier.CarrierIdentifier;
+import android.service.carrier.CarrierService;
+
+public class DummyCarrierConfigService extends CarrierService {
+ private static final String TAG = "DummyCarrierConfigService";
+
+ public DummyCarrierConfigService() {}
+
+ @Override
+ public PersistableBundle onLoadConfig(CarrierIdentifier id) {
+ return new PersistableBundle(); // Do nothing
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return super.onBind(intent);
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ return super.onUnbind(intent);
+ }
+}
+
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 0a143c5..778f0c5 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -796,14 +796,15 @@
// Make sure that the NC is null if the package doesn't hold ACCESS_NETWORK_STATE.
assertNull(redactNc(nc, groundedUid, groundedPkg));
- // Uids, ssid, underlying networks & subscriptionIds will be redacted if the given uid
+ // Uids, ssid & underlying networks will be redacted if the given uid
// doesn't hold the associated permissions. The wifi transport info is also suitably
// redacted.
final NetworkCapabilities redactedNormal = redactNc(nc, normalUid, normalPkg);
assertNull(redactedNormal.getUids());
assertNull(redactedNormal.getSsid());
assertNull(redactedNormal.getUnderlyingNetworks());
- assertEquals(0, redactedNormal.getSubscriptionIds().size());
+ // Owner UID is allowed to see the subscription IDs.
+ assertEquals(2, redactedNormal.getSubscriptionIds().size());
assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS,
((WifiInfo) redactedNormal.getTransportInfo()).getBSSID());
assertEquals(rssi, ((WifiInfo) redactedNormal.getTransportInfo()).getRssi());
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 392cba9..3b6607f 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -15,9 +15,12 @@
*/
package android.net.cts
+import android.Manifest.permission.MODIFY_PHONE_STATE
import android.Manifest.permission.NETWORK_SETTINGS
+import android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE
import android.app.Instrumentation
import android.content.Context
+import android.content.pm.PackageManager
import android.net.ConnectivityManager
import android.net.EthernetNetworkSpecifier
import android.net.INetworkAgent
@@ -44,7 +47,9 @@
import android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED
import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
+import android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
import android.net.NetworkCapabilities.TRANSPORT_TEST
import android.net.NetworkCapabilities.TRANSPORT_VPN
import android.net.NetworkCapabilities.TRANSPORT_WIFI
@@ -53,6 +58,7 @@
import android.net.NetworkReleasedException
import android.net.NetworkRequest
import android.net.NetworkScore
+import android.net.NetworkSpecifier
import android.net.QosCallback
import android.net.QosCallback.QosCallbackRegistrationException
import android.net.QosCallbackException
@@ -61,6 +67,7 @@
import android.net.QosSocketInfo
import android.net.RouteInfo
import android.net.SocketKeepalive
+import android.net.TelephonyNetworkSpecifier
import android.net.TestNetworkInterface
import android.net.TestNetworkManager
import android.net.Uri
@@ -69,21 +76,31 @@
import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnError
import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnQosSessionAvailable
import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnQosSessionLost
+import android.net.wifi.WifiInfo
import android.os.Build
+import android.os.ConditionVariable
import android.os.Handler
import android.os.HandlerThread
import android.os.Message
+import android.os.PersistableBundle
import android.os.Process
import android.os.SystemClock
import android.platform.test.annotations.AppModeFull
import android.system.OsConstants.IPPROTO_TCP
import android.system.OsConstants.IPPROTO_UDP
+import android.telephony.CarrierConfigManager
+import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
+import android.telephony.TelephonyManager.CarrierPrivilegesCallback
import android.telephony.data.EpsBearerQosSessionAttributes
+import android.util.ArraySet
import android.util.DebugUtils.valueToString
+import android.util.Log
import androidx.test.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
import com.android.compatibility.common.util.ThrowingSupplier
+import com.android.compatibility.common.util.UiccUtil
import com.android.modules.utils.build.SdkLevel
import com.android.net.module.util.ArrayTrackRecord
import com.android.testutils.CompatUtil
@@ -112,23 +129,8 @@
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnValidationStatus
import com.android.testutils.TestableNetworkCallback
import com.android.testutils.assertThrows
-import java.io.Closeable
-import java.io.IOException
-import java.net.DatagramSocket
-import java.net.InetAddress
-import java.net.InetSocketAddress
-import java.net.Socket
-import java.time.Duration
-import java.util.Arrays
-import java.util.UUID
-import java.util.concurrent.Executors
-import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
-import kotlin.test.assertFalse
-import kotlin.test.assertNotNull
-import kotlin.test.assertNull
-import kotlin.test.assertTrue
-import kotlin.test.fail
+import com.android.testutils.runAsShell
+import com.android.testutils.tryTest
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -140,7 +142,26 @@
import org.mockito.Mockito.mock
import org.mockito.Mockito.timeout
import org.mockito.Mockito.verify
+import java.io.Closeable
+import java.io.IOException
+import java.net.DatagramSocket
+import java.net.InetAddress
+import java.net.InetSocketAddress
+import java.net.Socket
+import java.security.MessageDigest
+import java.time.Duration
+import java.util.Arrays
+import java.util.UUID
+import java.util.concurrent.Executors
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+import kotlin.test.assertFalse
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+import kotlin.test.assertTrue
+import kotlin.test.fail
+private const val TAG = "NetworkAgentTest"
// This test doesn't really have a constraint on how fast the methods should return. If it's
// going to fail, it will simply wait forever, so setting a high timeout lowers the flake ratio
// without affecting the run time of successful runs. Thus, set a very high timeout.
@@ -261,17 +282,18 @@
callbacksToCleanUp.add(callback)
}
- private fun makeTestNetworkRequest(specifier: String? = null): NetworkRequest {
- return NetworkRequest.Builder()
- .clearCapabilities()
- .addTransportType(TRANSPORT_TEST)
- .also {
- if (specifier != null) {
- it.setNetworkSpecifier(CompatUtil.makeEthernetNetworkSpecifier(specifier))
- }
- }
- .build()
- }
+ private fun String?.asEthSpecifier(): NetworkSpecifier? =
+ if (null == this) null else CompatUtil.makeEthernetNetworkSpecifier(this)
+ private fun makeTestNetworkRequest(specifier: NetworkSpecifier? = null) =
+ NetworkRequest.Builder().run {
+ clearCapabilities()
+ addTransportType(TRANSPORT_TEST)
+ if (specifier != null) setNetworkSpecifier(specifier)
+ build()
+ }
+
+ private fun makeTestNetworkRequest(specifier: String?) =
+ makeTestNetworkRequest(specifier.asEthSpecifier())
private fun makeTestNetworkCapabilities(
specifier: String? = null,
@@ -322,7 +344,7 @@
): Pair<TestableNetworkAgent, TestableNetworkCallback> {
val callback = TestableNetworkCallback()
// Ensure this NetworkAgent is never unneeded by filing a request with its specifier.
- requestNetwork(makeTestNetworkRequest(specifier = specifier), callback)
+ requestNetwork(makeTestNetworkRequest(specifier), callback)
val nc = makeTestNetworkCapabilities(specifier, transports)
val agent = createNetworkAgent(context, initialConfig = initialConfig, initialNc = nc)
agent.setTeardownDelayMillis(0)
@@ -543,6 +565,227 @@
.addTransportType(TRANSPORT_TEST)
.setAllowedUids(uids.toSet()).build()
+ /**
+ * Get the single element from this ArraySet, or fail() if doesn't contain exactly 1 element.
+ */
+ fun <T> ArraySet<T>.getSingleElement(): T {
+ if (size != 1) fail("Expected exactly one element, contained $size")
+ return iterator().next()
+ }
+
+ private fun doTestAllowedUids(
+ subId: Int,
+ transport: Int,
+ uid: Int,
+ expectUidsPresent: Boolean
+ ) {
+ doTestAllowedUids(subId, intArrayOf(transport), uid, expectUidsPresent)
+ }
+
+ private fun doTestAllowedUids(
+ subId: Int,
+ transports: IntArray,
+ uid: Int,
+ expectUidsPresent: Boolean
+ ) {
+ val callback = TestableNetworkCallback(DEFAULT_TIMEOUT_MS)
+ val specifier = when {
+ transports.size != 1 -> null
+ TRANSPORT_ETHERNET in transports -> EthernetNetworkSpecifier("testInterface")
+ TRANSPORT_CELLULAR in transports -> TelephonyNetworkSpecifier(subId)
+ else -> null
+ }
+ val agent = createNetworkAgent(initialNc = NetworkCapabilities.Builder().run {
+ addTransportType(TRANSPORT_TEST)
+ transports.forEach { addTransportType(it) }
+ addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+ addCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ setNetworkSpecifier(specifier)
+ if (TRANSPORT_WIFI in transports && SdkLevel.isAtLeastV()) {
+ // setSubscriptionId only exists in V+
+ setTransportInfo(WifiInfo.Builder().setSubscriptionId(subId).build())
+ }
+ setAllowedUids(setOf(uid))
+ setOwnerUid(Process.myUid())
+ setAdministratorUids(intArrayOf(Process.myUid()))
+ build()
+ })
+ runWithShellPermissionIdentity {
+ agent.register()
+ }
+ agent.markConnected()
+
+ registerNetworkCallback(makeTestNetworkRequest(specifier), callback)
+ callback.expect<Available>(agent.network!!)
+ callback.expect<CapabilitiesChanged>(agent.network!!) {
+ if (expectUidsPresent) {
+ it.caps.allowedUidsNoCopy.getSingleElement() == uid
+ } else {
+ it.caps.allowedUidsNoCopy.isEmpty()
+ }
+ }
+ agent.unregister()
+ // callback will be unregistered in tearDown()
+ }
+
+ private fun setHoldCarrierPrivilege(hold: Boolean, subId: Int) {
+ fun getCertHash(): String {
+ val pkgInfo = realContext.packageManager.getPackageInfo(realContext.opPackageName,
+ PackageManager.GET_SIGNATURES)
+ val digest = MessageDigest.getInstance("SHA-256")
+ val certHash = digest.digest(pkgInfo.signatures!![0]!!.toByteArray())
+ return UiccUtil.bytesToHexString(certHash)!!
+ }
+
+ val tm = realContext.getSystemService(TelephonyManager::class.java)!!
+ val ccm = realContext.getSystemService(CarrierConfigManager::class.java)!!
+
+ val cv = ConditionVariable()
+ val cpb = PrivilegeWaiterCallback(cv)
+ tryTest {
+ val slotIndex = SubscriptionManager.getSlotIndex(subId)!!
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) {
+ tm.registerCarrierPrivilegesCallback(slotIndex, { it.run() }, cpb)
+ }
+ // Wait for the callback to be registered
+ assertTrue(cv.block(DEFAULT_TIMEOUT_MS), "Can't register CarrierPrivilegesCallback")
+ if (cpb.hasPrivilege == hold) {
+ if (hold) {
+ Log.w(TAG, "Package ${realContext.opPackageName} already is privileged")
+ } else {
+ Log.w(TAG, "Package ${realContext.opPackageName} already isn't privileged")
+ }
+ return@tryTest
+ }
+ cv.close()
+ runAsShell(MODIFY_PHONE_STATE) {
+ val carrierConfigs = if (hold) {
+ PersistableBundle().also {
+ it.putStringArray(CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY,
+ arrayOf(getCertHash()))
+ }
+ } else {
+ null
+ }
+ ccm.overrideConfig(subId, carrierConfigs)
+ }
+ assertTrue(cv.block(DEFAULT_TIMEOUT_MS), "Can't change carrier privilege")
+ } cleanup {
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) {
+ tm.unregisterCarrierPrivilegesCallback(cpb)
+ }
+ }
+ }
+
+ private fun acquireCarrierPrivilege(subId: Int) = setHoldCarrierPrivilege(true, subId)
+ private fun dropCarrierPrivilege(subId: Int) = setHoldCarrierPrivilege(false, subId)
+
+ private fun setCarrierServicePackageOverride(subId: Int, pkg: String?) {
+ val tm = realContext.getSystemService(TelephonyManager::class.java)!!
+
+ val cv = ConditionVariable()
+ val cpb = CarrierServiceChangedWaiterCallback(cv)
+ tryTest {
+ val slotIndex = SubscriptionManager.getSlotIndex(subId)!!
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) {
+ tm.registerCarrierPrivilegesCallback(slotIndex, { it.run() }, cpb)
+ }
+ // Wait for the callback to be registered
+ assertTrue(cv.block(DEFAULT_TIMEOUT_MS), "Can't register CarrierPrivilegesCallback")
+ if (cpb.pkgName == pkg) {
+ Log.w(TAG, "Carrier service package was already $pkg")
+ return@tryTest
+ }
+ cv.close()
+ runAsShell(MODIFY_PHONE_STATE) {
+ if (null == pkg) {
+ // There is a bug is clear-carrier-service-package-override where not adding
+ // the -s argument will use the wrong slot index : b/299604822
+ runShellCommand("cmd phone clear-carrier-service-package-override" +
+ " -s $subId")
+ } else {
+ // -s could set the subId, but this test works with the default subId.
+ runShellCommand("cmd phone set-carrier-service-package-override $pkg")
+ }
+ }
+ assertTrue(cv.block(DEFAULT_TIMEOUT_MS), "Can't modify carrier service package")
+ } cleanup {
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) {
+ tm.unregisterCarrierPrivilegesCallback(cpb)
+ }
+ }
+ }
+
+ private fun String.execute() = runShellCommand(this).trim()
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S)
+ fun testAllowedUids() {
+ // Use a different package than this one to make sure that a package that doesn't hold
+ // carrier service permission can be set as an allowed UID.
+ val servicePackage = "android.net.cts.carrierservicepackage"
+ val uid = try {
+ realContext.packageManager.getApplicationInfo(servicePackage, 0).uid
+ } catch (e: PackageManager.NameNotFoundException) {
+ fail("$servicePackage could not be installed, please check the SuiteApkInstaller" +
+ " installed CtsCarrierServicePackage.apk", e)
+ }
+
+ val tm = realContext.getSystemService(TelephonyManager::class.java)!!
+ val defaultSubId = SubscriptionManager.getDefaultSubscriptionId()
+ tryTest {
+ // This process is not the carrier service UID, so allowedUids should be ignored in all
+ // the following cases.
+ doTestAllowedUids(defaultSubId, TRANSPORT_CELLULAR, uid, expectUidsPresent = false)
+ doTestAllowedUids(defaultSubId, TRANSPORT_WIFI, uid, expectUidsPresent = false)
+ doTestAllowedUids(defaultSubId, TRANSPORT_BLUETOOTH, uid, expectUidsPresent = false)
+
+ // The tools to set the carrier service package override do not exist before U,
+ // so there is no way to test the rest of this test on < U.
+ if (!SdkLevel.isAtLeastU()) return@tryTest
+ // Acquiring carrier privilege is necessary to override the carrier service package.
+ val defaultSlotIndex = SubscriptionManager.getSlotIndex(defaultSubId)
+ acquireCarrierPrivilege(defaultSubId)
+ setCarrierServicePackageOverride(defaultSubId, servicePackage)
+ val actualServicePackage: String? = runAsShell(READ_PRIVILEGED_PHONE_STATE) {
+ tm.getCarrierServicePackageNameForLogicalSlot(defaultSlotIndex)
+ }
+ assertEquals(servicePackage, actualServicePackage)
+
+ // Wait for CarrierServiceAuthenticator to have seen the update of the service package
+ val timeout = SystemClock.elapsedRealtime() + DEFAULT_TIMEOUT_MS
+ while (true) {
+ if (SystemClock.elapsedRealtime() > timeout) {
+ fail("Couldn't make $servicePackage the service package for $defaultSubId: "
+ + "dumpsys connectivity".execute().split("\n")
+ .filter { it.contains("Logical slot = $defaultSlotIndex.*") })
+ }
+ if ("dumpsys connectivity"
+ .execute()
+ .split("\n")
+ .filter { it.contains("Logical slot = $defaultSlotIndex : uid = $uid") }
+ .isNotEmpty()) {
+ // Found the configuration
+ break
+ }
+ Thread.sleep(500)
+ }
+
+ // Cell is allowed to set UIDs, but not WIFI/BLUETOOTH or agents with multiple
+ // transports.
+ doTestAllowedUids(defaultSubId, TRANSPORT_CELLULAR, uid, expectUidsPresent = true)
+ doTestAllowedUids(defaultSubId, TRANSPORT_WIFI, uid, expectUidsPresent = false)
+ doTestAllowedUids(defaultSubId, TRANSPORT_BLUETOOTH, uid, expectUidsPresent = false)
+ doTestAllowedUids(defaultSubId, intArrayOf(TRANSPORT_CELLULAR, TRANSPORT_WIFI), uid,
+ expectUidsPresent = false)
+ } cleanupStep {
+ if (SdkLevel.isAtLeastU()) setCarrierServicePackageOverride(defaultSubId, null)
+ } cleanup {
+ if (SdkLevel.isAtLeastU()) dropCarrierPrivilege(defaultSubId)
+ }
+ }
+
@Test
fun testRejectedUpdates() {
val callback = TestableNetworkCallback(DEFAULT_TIMEOUT_MS)
@@ -1497,3 +1740,25 @@
doTestNativeNetworkCreation(expectCreatedImmediately = true, intArrayOf(TRANSPORT_VPN))
}
}
+
+// Subclasses of CarrierPrivilegesCallback can't be inline, or they'll be compiled as
+// inner classes of the test class and will fail resolution on R as the test harness
+// uses reflection to list all methods and classes
+class PrivilegeWaiterCallback(private val cv: ConditionVariable) :
+ CarrierPrivilegesCallback {
+ var hasPrivilege = false
+ override fun onCarrierPrivilegesChanged(p: MutableSet<String>, uids: MutableSet<Int>) {
+ hasPrivilege = uids.contains(Process.myUid())
+ cv.open()
+ }
+}
+
+class CarrierServiceChangedWaiterCallback(private val cv: ConditionVariable) :
+ CarrierPrivilegesCallback {
+ var pkgName: String? = null
+ override fun onCarrierPrivilegesChanged(p: MutableSet<String>, u: MutableSet<Int>) {}
+ override fun onCarrierServiceChanged(pkgName: String?, uid: Int) {
+ this.pkgName = pkgName
+ cv.open()
+ }
+}
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index b8cf08e..b076fe5 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -17386,6 +17386,7 @@
mCm.requestNetwork(new NetworkRequest.Builder()
.clearCapabilities()
.addTransportType(TRANSPORT_TEST)
+ .addTransportType(TRANSPORT_CELLULAR)
.build(),
cb);