diff --git a/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt b/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt index ae0de794b9..c9d2527e4a 100644 --- a/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt +++ b/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt @@ -18,20 +18,28 @@ package com.android.testutils.com.android.testutils 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 @@ private const val CARRIER_CONFIG_CHANGE_TIMEOUT_MS = 10_000L * 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() + // Map of (subId) -> (original values of carrier service package) + private val originalCarrierServicePackages = mutableMapOf() + override fun apply(base: Statement, description: Description): Statement { return CarrierConfigStatement(base, description) } @@ -118,6 +134,177 @@ class CarrierConfigRule : TestRule { } } + 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, uids: MutableSet) { + hasPrivilege = uids.contains(Process.myUid()) + cv.open() + } + } + + private class CarrierServiceChangedWaiterCallback(private val cv: ConditionVariable) : + CarrierPrivilegesCallback { + var pkgName: String? = null + override fun onCarrierPrivilegesChanged(p: MutableSet, u: MutableSet) {} + 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 @@ class CarrierConfigRule : TestRule { } originalConfigs.clear() } + originalCarrierServicePackages.forEach { (subId, pkg) -> + setCarrierServicePackageOverride(subId, pkg) + } + originalCarrierServicePackages.clear() } } @@ -145,7 +336,7 @@ private fun CarrierConfigManager.getConfigForSubIdCompat( subId: Int, keys: Set ): 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 5e035a2723..6dc23ff527 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.OnQosS 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.AF_INET6 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.InetAddress 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 @@ class NetworkAgentTest { 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 @@ class NetworkAgentTest { 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 @@ class NetworkAgentTest { 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 @@ class NetworkAgentTest { 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, uids: MutableSet) { - hasPrivilege = uids.contains(Process.myUid()) - cv.open() - } -} - -class CarrierServiceChangedWaiterCallback(private val cv: ConditionVariable) : - CarrierPrivilegesCallback { - var pkgName: String? = null - override fun onCarrierPrivilegesChanged(p: MutableSet, u: MutableSet) {} - override fun onCarrierServiceChanged(pkgName: String?, uid: Int) { - this.pkgName = pkgName - cv.open() - } -}