#android #phone-call #sim-card #incallservice
Вопрос:
Предыстория
Я работаю над приложением, которое реализует InCallService, чтобы оно могло обрабатывать телефонные звонки
Проблема
На устройствах с несколькими SIM-картами мне нужно показать информацию о том, какая SIM-карта и связанный с ней номер телефона используются (текущего устройства).
Дело в том, что я не могу найти, где получить эту информацию.
Что я нашел
- Учитывая, что я достигаю функции, подобной onCallAdded, я получаю экземпляр класса Call, поэтому мне нужно связать то, что я получаю оттуда, со слотом sim и номером телефона.
- Используя
call.getDetails().getHandle()
, я могу получить Uri, состоящий только из номера телефона другого человека, который звонил (который звонил текущему пользователю или кому звонил текущий пользователь). - Я могу перебрать все SIM-карты, но не могу найти то, что я могу использовать для сопоставления между ними и текущим вызовом:
final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
final SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
for (final SubscriptionInfo subscriptionInfo : subscriptionManager.getActiveSubscriptionInfoList()) {
final TelephonyManager subscriptionId = telephonyManager.createForSubscriptionId(subscriptionInfo.getSubscriptionId());
}
- Был старый код, который больше не работает, который использует
call.getDetails().getAccountHandle().getId()
иSubscriptionManager.from(context)getActiveSubscriptionInfoList().getActiveSubscriptionInfoList()
. - Я думаю, что могу использовать
telephonyManager.getSubscriptionId(callDetails.getAccountHandle())
, но для этого требуется API 30, что довольно ново…
Вопросы
Учитывая телефонный звонок, который я получаю от этого обратного вызова (и, возможно, других), как я могу получить соответствующий слот для SIM-карты и номер его телефона?
Я предпочитаю знать, как это сделать для максимально широкого диапазона версий Android, потому что InCallService из API 23… Это должно быть возможно до API 30, верно?
Комментарии:
1.
TelecomManager.getPhoneAccount()
Работает ли?2. @vlp Кажется, что это действительно работает хорошо! К сожалению, я не могу протестировать старые устройства (у них их нет), а эмулятор не может иметь multi-SIM, но это выглядит многообещающе. Я использовал это, чтобы получить номер телефона: telecomManager.getPhoneAccount(call.details.accountHandle)? .адрес?.schemeSpecificPart а затем я нашел его в списке SubscriptionManager.activeSubscriptionInfoList в числовом поле. Почему вы не написали это в ответах? И как вы об этом узнали? Я пытался искать об этом часами…
3. Честно говоря, я читал об этом в официальной документации и никогда не пробовал. Я рад, что это работает для вас. Удачи в вашем проекте!
4. @vlp Пожалуйста, напишите в своем ответе, откуда вы его получили.
5. У меня есть эта информация исключительно из официальной документации Android.
Ответ №1:
Используйте call.getDetails().getAccountHandle()
для получения PhoneAccountHandle.
Затем используйте TelecomManager.getPhoneAccount(), чтобы получить экземпляр PhoneAccount.
Разрешение Manifest.permission.READ_PHONE_NUMBERS
требуется для приложений, ориентированных на уровень API 31 .
Отказ от ответственности: я не эксперт по Android, поэтому, пожалуйста, подтвердите мои мысли.
РЕДАКТИРОВАТЬ: таким образом, решение как для API 30, так и для API 30 может быть таким:
@RequiresApi(Build.VERSION_CODES.M)
fun handleCall(context: Context, call: Call) {
var foundAndSetSimDetails = false
val callDetailsAccountHandle = callDetails.accountHandle
val subscriptionManager = context
.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
val telephonyManager =
context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
val telecomManager =
context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
val hasReadPhoneStatePermission =
ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == android.content.pm.PackageManager.PERMISSION_GRANTED
val phoneAccount = telecomManager.getPhoneAccount(callDetailsAccountHandle)
//TODO when targeting API 31, we might need to check for a new permission here, of READ_PHONE_NUMBERS
//find SIM by phone account
if (!foundAndSetSimDetails amp;amp; phoneAccount != null amp;amp; hasReadPhoneStatePermission) {
val callCapablePhoneAccounts = telecomManager.callCapablePhoneAccounts
run {
callCapablePhoneAccounts?.forEachIndexed { index, phoneAccountHandle ->
if (phoneAccountHandle != callDetailsAccountHandle)
return@forEachIndexed
if (!phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION))
return@run
//found the sim card index
simName = phoneAccount.label?.toString().orEmpty()
simIndex = index 1
foundAndSetSimDetails = true
return@run
}
}
}
//find SIM by subscription ID
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R amp;amp; hasReadPhoneStatePermission) {
try {
val callSubscriptionId: Int =
telephonyManager.getSubscriptionId(callDetailsAccountHandle!!)
for (subscriptionInfo: SubscriptionInfo in subscriptionManager.activeSubscriptionInfoList) {
val activeSubscriptionId: Int = subscriptionInfo.subscriptionId
if (activeSubscriptionId == callSubscriptionId) {
setSimDetails(telephonyManager, subscriptionInfo)
foundAndSetSimDetails = true
break
}
}
} catch (e: Throwable) {
e.printStackTrace()
}
}
//find SIM by phone number
if (!foundAndSetSimDetails amp;amp; hasReadPhoneStatePermission) {
try {
val simPhoneNumber: String? = phoneAccount?.address?.schemeSpecificPart
if (!simPhoneNumber.isNullOrBlank()) {
for (subscriptionInfo in subscriptionManager.activeSubscriptionInfoList) {
if (simPhoneNumber == subscriptionInfo.number) {
setSimDetails(telephonyManager, subscriptionInfo)
foundAndSetSimDetails = true
break
}
}
if (!foundAndSetSimDetails)
}
} catch (e: Throwable) {
e.printStackTrace()
}
}
private fun setSimDetails(telephonyManager: TelephonyManager, subscriptionInfo: SubscriptionInfo) {
var foundSimName: String? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val telephonyManagerForSubscriptionId =
telephonyManager.createForSubscriptionId(subscriptionInfo.subscriptionId)
foundSimName = telephonyManagerForSubscriptionId.simOperatorName
}
if (foundSimName.isNullOrBlank())
foundSimName = subscriptionInfo.carrierName?.toString()
simName = if (foundSimName.isNullOrBlank())
""
else foundSimName
simIndex = subscriptionInfo.simSlotIndex 1
}
Комментарии:
1. Для какой функции, как вы видите, нам нужно
READ_PHONE_NUMBERS
разрешение? Кроме того, потребуется ли это, даже если я использую относительно новый API, oftelephonyManager.getSubscriptionId
, который я нахожу в спискеsubscriptionManager.activeSubscriptionInfoList
элементов, проверяя ихsubscriptionId
?2. В документации указано, что
TelecomManager.getPhoneAccount()
требуетсяREAD_PHONE_NUMBERS
на уровне API 31 ,TelephonyManager.getSubscriptionId
потребностиREAD_PHONE_STATE
иSubscriptionManager.getActiveSubscriptionInfoList()
потребностиREAD_PHONE_STATE
или привилегии оператора. Также обратите внимание, чтоSubscriptionInfo.getNumber()
для планируемого использования требуются некоторые разрешения (READ_PHONE_STATE
ниже уровня API 30 или одно изREAD_PRIVILEGED_PHONE_STATE
/READ_PHONE_NUMBERS
/READ_SMS
на уровне API 30 ) или привилегии оператора.3. Итак, для
telephonyManager.getSubscriptionId(callDetails.getAccountHandle())
последующего поиска вSubscriptionManager.getActiveSubscriptionInfoList()
подходе вам теоретически потребуетсяREAD_PHONE_STATE
(TelephonyManager.getSubscriptionId
SubscriptionManager.getActiveSubscriptionInfoList()
), а затем соответствующие привилегии для чтения информации изSubsciptionInfo
(READ_PRIVILEGED_PHONE_STATE
/READ_PHONE_NUMBERS
/READ_SMS
, чтобы получить номер телефона). Предполагается, что уровень API 30 . Пожалуйста, перекрестно подтвердите мои мысли.4. Я отредактировал ваш ответ, чтобы включить полный код для обоих случаев. Что касается разрешений, спасибо. В обоих случаях требуется READ_PHONE_STATE при настройке API 30. Я думаю, что при таргетинге на API 31 только
getPhoneAccount
это будет проблематично, но тогда оно используется только для случая до API 30, так что все должно быть в порядке. Я попробовал сейчас, и он предупредил меня только оREAD_PHONE_STATE
разрешении, поэтому не знаю, есть ли в этом коде больше возможных проблем. Сообщается здесь: issuetracker.google.com/issues/205726880 . Если у вас есть какие-либо другие вещи, которые вы считаете важными, пожалуйста, дайте мне знать.5. Google ответил мне о том, как это сделать, но не уверен в том, что они написали: issuetracker.google.com/issues/204791554#comment5