Add carrier privilege utils to CarrierConfigRule
Move the existing helper methods to acquire carrier privileges in
NetworkAgentTest.kt to CarrierConfigRule where it can be reused by other
CTS tests. Also reset the carrier service package override in the rule
cleanup.
Bug: 216524590
Test: atest NetworkAgentTest
Change-Id: Id907a2d641aa61838119e2be65654b9796464498
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt b/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt
index ae0de79..c9d2527 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt
@@ -18,20 +18,28 @@
import android.Manifest.permission.MODIFY_PHONE_STATE
import android.Manifest.permission.READ_PHONE_STATE
+import android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
+import android.content.pm.PackageManager
import android.os.ConditionVariable
+import android.os.ParcelFileDescriptor
import android.os.PersistableBundle
+import android.os.Process
import android.telephony.CarrierConfigManager
import android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED
+import android.telephony.SubscriptionManager
import android.telephony.SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX
+import android.telephony.TelephonyManager
+import android.telephony.TelephonyManager.CarrierPrivilegesCallback
import android.util.Log
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.modules.utils.build.SdkLevel.isAtLeastU
+import com.android.modules.utils.build.SdkLevel
import com.android.testutils.runAsShell
import com.android.testutils.tryTest
+import java.security.MessageDigest
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import org.junit.rules.TestRule
@@ -46,12 +54,20 @@
* configuration automatically on teardown.
*/
class CarrierConfigRule : TestRule {
+ private val HEX_CHARS: CharArray = charArrayOf(
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ )
+
private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
+ private val uiAutomation by lazy { InstrumentationRegistry.getInstrumentation().uiAutomation }
private val ccm by lazy { context.getSystemService(CarrierConfigManager::class.java) }
// Map of (subId) -> (original values of overridden settings)
private val originalConfigs = mutableMapOf<Int, PersistableBundle>()
+ // Map of (subId) -> (original values of carrier service package)
+ private val originalCarrierServicePackages = mutableMapOf<Int, String?>()
+
override fun apply(base: Statement, description: Description): Statement {
return CarrierConfigStatement(base, description)
}
@@ -118,6 +134,177 @@
}
}
+ private fun runShellCommand(cmd: String) {
+ val fd: ParcelFileDescriptor = uiAutomation.executeShellCommand(cmd)
+ fd.close() // Don't care about the output.
+ }
+
+ /**
+ * Converts a byte array into a String of hexadecimal characters.
+ *
+ * @param bytes an array of bytes
+ * @return hex string representation of bytes array
+ */
+ private fun bytesToHexString(bytes: ByteArray?): String? {
+ if (bytes == null) return null
+
+ val ret = StringBuilder(2 * bytes.size)
+
+ for (i in bytes.indices) {
+ var b: Int
+ b = 0x0f and (bytes[i].toInt() shr 4)
+ ret.append(HEX_CHARS[b])
+ b = 0x0f and bytes[i].toInt()
+ ret.append(HEX_CHARS[b])
+ }
+
+ return ret.toString()
+ }
+
+ private fun setHoldCarrierPrivilege(hold: Boolean, subId: Int) {
+ if (!SdkLevel.isAtLeastT()) {
+ throw UnsupportedOperationException(
+ "Acquiring carrier privilege requires at least T SDK"
+ )
+ }
+
+ fun getCertHash(): String {
+ val pkgInfo = context.packageManager.getPackageInfo(
+ context.opPackageName,
+ PackageManager.GET_SIGNATURES
+ )
+ val digest = MessageDigest.getInstance("SHA-256")
+ val certHash = digest.digest(pkgInfo.signatures!![0]!!.toByteArray())
+ return bytesToHexString(certHash)!!
+ }
+
+ val tm = context.getSystemService(TelephonyManager::class.java)!!
+
+ val cv = ConditionVariable()
+ val cpb = PrivilegeWaiterCallback(cv)
+ // The lambda below is capturing |cpb|, whose type inherits from a class that appeared in
+ // T. This means the lambda will compile as a private method of this class taking a
+ // PrivilegeWaiterCallback argument. As JUnit uses reflection to enumerate all methods
+ // including private methods, this would fail with a link error when running on S-.
+ // To solve this, make the lambda serializable, which causes the compiler to emit a
+ // synthetic class instead of a synthetic method.
+ tryTest @JvmSerializableLambda {
+ val slotIndex = SubscriptionManager.getSlotIndex(subId)!!
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
+ tm.registerCarrierPrivilegesCallback(slotIndex, { it.run() }, cpb)
+ }
+ // Wait for the callback to be registered
+ assertTrue(cv.block(CARRIER_CONFIG_CHANGE_TIMEOUT_MS),
+ "Can't register CarrierPrivilegesCallback")
+ if (cpb.hasPrivilege == hold) {
+ if (hold) {
+ Log.w(TAG, "Package ${context.opPackageName} already is privileged")
+ } else {
+ Log.w(TAG, "Package ${context.opPackageName} already isn't privileged")
+ }
+ return@tryTest
+ }
+ if (hold) {
+ addConfigOverrides(subId, PersistableBundle().also {
+ it.putStringArray(CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY,
+ arrayOf(getCertHash()))
+ })
+ } else {
+ cleanUpNow()
+ }
+ } cleanup @JvmSerializableLambda {
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
+ tm.unregisterCarrierPrivilegesCallback(cpb)
+ }
+ }
+ }
+
+ /**
+ * Acquires carrier privilege on the given subscription ID.
+ */
+ fun acquireCarrierPrivilege(subId: Int) = setHoldCarrierPrivilege(true, subId)
+
+ /**
+ * Drops carrier privilege from the given subscription ID.
+ */
+ fun dropCarrierPrivilege(subId: Int) = setHoldCarrierPrivilege(false, subId)
+
+ /**
+ * Sets the carrier service package override for the given subscription ID. A null argument will
+ * clear any previously-set override.
+ */
+ fun setCarrierServicePackageOverride(subId: Int, pkg: String?) {
+ if (!SdkLevel.isAtLeastU()) {
+ throw UnsupportedOperationException(
+ "Setting carrier service package override requires at least U SDK"
+ )
+ }
+
+ val tm = context.getSystemService(TelephonyManager::class.java)!!
+
+ val cv = ConditionVariable()
+ val cpb = CarrierServiceChangedWaiterCallback(cv)
+ // The lambda below is capturing |cpb|, whose type inherits from a class that appeared in
+ // T. This means the lambda will compile as a private method of this class taking a
+ // PrivilegeWaiterCallback argument. As JUnit uses reflection to enumerate all methods
+ // including private methods, this would fail with a link error when running on S-.
+ // To solve this, make the lambda serializable, which causes the compiler to emit a
+ // synthetic class instead of a synthetic method.
+ tryTest @JvmSerializableLambda {
+ val slotIndex = SubscriptionManager.getSlotIndex(subId)!!
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
+ tm.registerCarrierPrivilegesCallback(slotIndex, { it.run() }, cpb)
+ }
+ // Wait for the callback to be registered
+ assertTrue(cv.block(CARRIER_CONFIG_CHANGE_TIMEOUT_MS),
+ "Can't register CarrierPrivilegesCallback")
+ if (cpb.pkgName == pkg) {
+ Log.w(TAG, "Carrier service package was already $pkg")
+ return@tryTest
+ }
+ if (!originalCarrierServicePackages.contains(subId)) {
+ originalCarrierServicePackages.put(subId, cpb.pkgName)
+ }
+ cv.close()
+ runAsShell(MODIFY_PHONE_STATE) {
+ if (null == pkg) {
+ // There is a bug in 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 {
+ runShellCommand("cmd phone set-carrier-service-package-override $pkg" +
+ " -s $subId")
+ }
+ }
+ assertTrue(cv.block(CARRIER_CONFIG_CHANGE_TIMEOUT_MS),
+ "Can't modify carrier service package")
+ } cleanup @JvmSerializableLambda {
+ runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
+ tm.unregisterCarrierPrivilegesCallback(cpb)
+ }
+ }
+ }
+
+ private 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()
+ }
+ }
+
+ private 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()
+ }
+ }
+
/**
* Cleanup overrides that were added by the test case.
*
@@ -138,6 +325,10 @@
}
originalConfigs.clear()
}
+ originalCarrierServicePackages.forEach { (subId, pkg) ->
+ setCarrierServicePackageOverride(subId, pkg)
+ }
+ originalCarrierServicePackages.clear()
}
}
@@ -145,7 +336,7 @@
subId: Int,
keys: Set<String>
): PersistableBundle {
- return if (isAtLeastU()) {
+ return if (SdkLevel.isAtLeastU()) {
// This method is U+
getConfigForSubId(subId, *keys.toTypedArray())
} else {
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 5e035a2..6dc23ff 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -15,7 +15,6 @@
*/
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
@@ -80,12 +79,10 @@
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.Looper
import android.os.Message
-import android.os.PersistableBundle
import android.os.Process
import android.os.SystemClock
import android.platform.test.annotations.AppModeFull
@@ -94,19 +91,15 @@
import android.system.OsConstants.IPPROTO_TCP
import android.system.OsConstants.IPPROTO_UDP
import android.system.OsConstants.SOCK_DGRAM
-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.net.module.util.NetworkStackConstants.ETHER_MTU
@@ -151,7 +144,6 @@
import java.net.InetSocketAddress
import java.net.Socket
import java.nio.ByteBuffer
-import java.security.MessageDigest
import java.time.Duration
import java.util.Arrays
import java.util.Random
@@ -708,102 +700,6 @@
doTestAllowedUids(transports, uid, expectUidsPresent, specifier, transportInfo)
}
- 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 cv = ConditionVariable()
- val cpb = PrivilegeWaiterCallback(cv)
- // The lambda below is capturing |cpb|, whose type inherits from a class that appeared in
- // T. This means the lambda will compile as a private method of this class taking a
- // PrivilegeWaiterCallback argument. As JUnit uses reflection to enumerate all methods
- // including private methods, this would fail with a link error when running on S-.
- // To solve this, make the lambda serializable, which causes the compiler to emit a
- // synthetic class instead of a synthetic method.
- tryTest @JvmSerializableLambda {
- val slotIndex = SubscriptionManager.getSlotIndex(subId)!!
- runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
- 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
- }
- if (hold) {
- carrierConfigRule.addConfigOverrides(subId, PersistableBundle().also {
- it.putStringArray(CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY,
- arrayOf(getCertHash()))
- })
- } else {
- carrierConfigRule.cleanUpNow()
- }
- } cleanup @JvmSerializableLambda {
- runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
- 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)
- // The lambda below is capturing |cpb|, whose type inherits from a class that appeared in
- // T. This means the lambda will compile as a private method of this class taking a
- // PrivilegeWaiterCallback argument. As JUnit uses reflection to enumerate all methods
- // including private methods, this would fail with a link error when running on S-.
- // To solve this, make the lambda serializable, which causes the compiler to emit a
- // synthetic class instead of a synthetic method.
- tryTest @JvmSerializableLambda {
- val slotIndex = SubscriptionManager.getSlotIndex(subId)!!
- runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
- 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 @JvmSerializableLambda {
- runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
- tm.unregisterCarrierPrivilegesCallback(cpb)
- }
- }
- }
-
private fun String.execute() = runShellCommand(this).trim()
@Test
@@ -856,8 +752,8 @@
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)
+ carrierConfigRule.acquireCarrierPrivilege(defaultSubId)
+ carrierConfigRule.setCarrierServicePackageOverride(defaultSubId, servicePackage)
val actualServicePackage: String? = runAsShell(READ_PRIVILEGED_PHONE_STATE) {
tm.getCarrierServicePackageNameForLogicalSlot(defaultSlotIndex)
}
@@ -896,10 +792,6 @@
expectUidsPresent = false)
doTestAllowedUidsWithSubId(defaultSubId, intArrayOf(TRANSPORT_CELLULAR, TRANSPORT_WIFI),
uid, expectUidsPresent = false)
- } cleanupStep {
- if (SdkLevel.isAtLeastU()) setCarrierServicePackageOverride(defaultSubId, null)
- } cleanup {
- if (SdkLevel.isAtLeastU()) dropCarrierPrivilege(defaultSubId)
}
}
@@ -2001,25 +1893,3 @@
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()
- }
-}