Merge "Add carrier privilege utils to CarrierConfigRule" into main
This commit is contained in:
commit
ab6342b70e
2 changed files with 195 additions and 134 deletions
|
|
@ -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<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 @@ 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<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 @@ 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<String>
|
||||
): PersistableBundle {
|
||||
return if (isAtLeastU()) {
|
||||
return if (SdkLevel.isAtLeastU()) {
|
||||
// This method is U+
|
||||
getConfigForSubId(subId, *keys.toTypedArray())
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -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<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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue