Merge rvc-qpr-dev-plus-aosp-without-vendor@6881855
Bug: 172690556
Merged-In: I3c472c1178a6c68f524785a5362524ede1d7a158
Change-Id: I831ca4a17b75e84674d2cf7443d6e3f4d64c59c4
diff --git a/Android.bp b/Android.bp
index 1887a8a..e7ca068 100644
--- a/Android.bp
+++ b/Android.bp
@@ -35,6 +35,7 @@
"com.android.phone.common-lib",
"guava",
"PlatformProperties",
+ "modules-utils-os",
],
srcs: [
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 0465d49..e38ea00 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -82,6 +82,7 @@
<protected-broadcast android:name= "com.android.cellbroadcastreceiver.GET_LATEST_CB_AREA_INFO" />
<protected-broadcast android:name= "com.android.internal.telephony.ACTION_CARRIER_CERTIFICATE_DOWNLOAD" />
<protected-broadcast android:name= "com.android.internal.telephony.OPEN_DEFAULT_SMS_APP" />
+ <protected-broadcast android:name= "com.android.internal.telephony.ACTION_TEST_OVERRIDE_CARRIER_ID" />
<protected-broadcast android:name= "android.telephony.action.SIM_CARD_STATE_CHANGED" />
<protected-broadcast android:name= "android.telephony.action.SIM_APPLICATION_STATE_CHANGED" />
<protected-broadcast android:name= "android.telephony.action.SIM_SLOT_STATUS_CHANGED" />
@@ -219,6 +220,7 @@
<uses-permission android:name="android.permission.NETWORK_STATS_PROVIDER" />
<uses-permission android:name="android.permission.HANDLE_CAR_MODE_CHANGES"/>
<uses-permission android:name="android.permission.MANAGE_SUBSCRIPTION_PLANS"/>
+ <uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS"/>
<application android:name="PhoneApp"
android:persistent="true"
@@ -237,6 +239,10 @@
android:readPermission="android.permission.READ_CONTACTS"
android:writePermission="android.permission.WRITE_CONTACTS" />
+ <provider android:name="com.android.ims.rcs.uce.eab.EabProvider"
+ android:authorities="eab"
+ android:exported="false"/>
+
<!-- Dialer UI that only allows emergency calls -->
<activity android:name="EmergencyDialer"
android:label="@string/emergencyDialerIconLabel"
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 74169e6..c50abdc 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -95,7 +95,7 @@
<string name="sum_loading_settings" msgid="434063780286688775">"Laai tans instellings..."</string>
<string name="sum_hide_caller_id" msgid="131100328602371933">"Nommer versteek in uitgaande oproepe"</string>
<string name="sum_show_caller_id" msgid="3571854755324664591">"Nommer wat vertoon in uitgaande oproepe"</string>
- <string name="sum_default_caller_id" msgid="1767070797135682959">"Gebruik verstek beheerinstellings om my nommer te vertoon in uitgaande oproepe"</string>
+ <string name="sum_default_caller_id" msgid="1767070797135682959">"Gebruik verstekbeheerinstellings om my nommer te vertoon in uitgaande oproepe"</string>
<string name="labelCW" msgid="8449327023861428622">"Oproep wag"</string>
<string name="sum_cw_enabled" msgid="3977308526187139996">"Stel my tydens \'n oproep in kennis van inkomende oproepe"</string>
<string name="sum_cw_disabled" msgid="3658094589461768637">"Stel my tydens \'n oproep in kennis van inkomende oproepe"</string>
@@ -141,7 +141,7 @@
<string name="close_dialog" msgid="1074977476136119408">"OK"</string>
<string name="enable" msgid="2636552299455477603">"Skakel aan"</string>
<string name="disable" msgid="1122698860799462116">"Skakel af"</string>
- <string name="change_num" msgid="6982164494063109334">"Opdateer"</string>
+ <string name="change_num" msgid="6982164494063109334">"Dateer op"</string>
<string-array name="clir_display_values">
<item msgid="8477364191403806960">"Netwerkverstek"</item>
<item msgid="6813323051965618926">"Versteek nommer"</item>
@@ -863,7 +863,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Sub-ID van verstekdata-SIM:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Aflaaibandwydte (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Oplaaibandwydte (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Selligginginligting (opgeskort):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Fisieke LTE-kanaalopstelling:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Herlaaikoers van selinligting:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Alle selmetingsinligting:"</string>
diff --git a/res/values-am/arrays.xml b/res/values-am/arrays.xml
index f5cfebf..9d43d58 100644
--- a/res/values-am/arrays.xml
+++ b/res/values-am/arrays.xml
@@ -18,7 +18,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="preferred_network_mode_choices_world_mode">
- <item msgid="8359756926680194770">"አለምአቀፍ"</item>
+ <item msgid="8359756926680194770">"ዓለምአቀፍ"</item>
<item msgid="7497128470871581354">"LTE / CDMA"</item>
<item msgid="1732367262765147258">"LTE / GSM / UMTS"</item>
</string-array>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index cecf2aa..3575df4 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -74,7 +74,7 @@
<string name="phone_accounts_configure_account_settings" msgid="6622119715253196586">"የመለያ ቅንብሮችን ይውቀሩ"</string>
<string name="phone_accounts_all_calling_accounts" msgid="1609600743500618823">"የሁሉም ደዋይ መለያዎች"</string>
<string name="phone_accounts_all_calling_accounts_summary" msgid="2214134955430107240">"የትኛዎቹ መለያዎች ጥሪዎችን ማድረግ እንደሚችሉ ይምረጡ"</string>
- <string name="wifi_calling" msgid="3650509202851355742">"የWi-Fi ጥሪ ማድረጊያ"</string>
+ <string name="wifi_calling" msgid="3650509202851355742">"የWi-Fi ጥሪ"</string>
<string name="connection_service_default_label" msgid="7332739049855715584">"አብሮ የተገነባ የግንኙነት አገልግሎት"</string>
<string name="voicemail" msgid="7697769412804195032">"የድምፅ መልዕክት"</string>
<string name="voicemail_settings_with_label" msgid="4228431668214894138">"የድምጽ መልዕክት (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
@@ -236,7 +236,7 @@
<string name="preferred_network_mode_lte_gsm_wcdma_summary" msgid="2217794334331254936">"ተመራጭ የአውታረመረብ ሁኔታ: GSM/WCDMA/LTE"</string>
<string name="preferred_network_mode_lte_cdma_evdo_summary" msgid="5559198623419981805">"ተመራጭ የአውታረመረብ ሁኔታ ፡ CDMA+LTE/EVDO"</string>
<string name="preferred_network_mode_lte_cdma_evdo_gsm_wcdma_summary" msgid="6707224437925495615">"የተመረጠው የአውታረ መረብ ሁነታ፦ LTE/CDMA/EvDo/GSM/WCDMA"</string>
- <string name="preferred_network_mode_global_summary" msgid="3847086258439582411">"ተመራጭ የአውታረመረብ ፡ አለምአቀፍ"</string>
+ <string name="preferred_network_mode_global_summary" msgid="3847086258439582411">"ተመራጭ የአውታረመረብ ፡ ዓለምአቀፍ"</string>
<string name="preferred_network_mode_lte_wcdma_summary" msgid="7001804022020813865">"ተመራጭ የአውታረመረብ ሁኔታ፡ LTE / WCDMA"</string>
<string name="preferred_network_mode_lte_gsm_umts_summary" msgid="6484203890156282179">"ተመራጭ የአውታረ መረብ ሁነታ፦ LTE / GSM / UMTS"</string>
<string name="preferred_network_mode_lte_cdma_summary" msgid="8187929456614068518">"ተመራጭ የአውታረመረብ ፡LTE / CDMA ሁነታ"</string>
@@ -403,7 +403,7 @@
<string name="network_recommended" msgid="3444321100580250926">" (የሚመከር)"</string>
<string name="network_lte" msgid="7206879277095094280">"LTE (የሚመከር)"</string>
<string name="network_4G" msgid="6800527815504223913">"4G (የሚመከር)"</string>
- <string name="network_global" msgid="3289646154407617631">"አለምአቀፍ"</string>
+ <string name="network_global" msgid="3289646154407617631">"ዓለምአቀፍ"</string>
<string name="cdma_system_select_title" msgid="614165233552656431">"የሥርዓት ምርጫ"</string>
<string name="cdma_system_select_summary" msgid="3840420390242060407">"የCDMA በእንቅስቃሴ ላይ ሁኔታ ለውጥ"</string>
<string name="cdma_system_select_dialogtitle" msgid="5524639510676501802">"የሥርዓት ምርጫ"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"የነባሪ ውሂብ ሲም SubId:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL መተላለፊያ ይዘት (ኪቢ/ሴ)፡"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"የUL መተላለፊያ ይዘት (ኪቢ/ሴ)፡"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"የሕዋስ የአካባቢ መረጃ (የተቋረጠ)፦"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"የLTE አካላዊ ሰርጥ ውቅረት:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"የሕዋስ መረጃ እድሳት ፍጥነት፡"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"ሁሉም የሕዋስ መለኪያ መረጃ፡"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index f220afe..79be3d5 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"ডিফ’ল্ট ডেটা ছিমৰ ছাবআইডি:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL বেণ্ডৱিথ (কেবিপিএছ):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL বেণ্ডৱিথ (কেবিপিএছ):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"চেল অৱস্থানৰ তথ্য (অনুমোদিত নহয়):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE ফিজিকেল চেনেলৰ কনফিগাৰেশ্বন:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"চেল তথ্য ৰিফ্ৰেশ্বৰ হাৰ:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"সকলো চেল পৰিমাপৰ তথ্য:"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index a053ece..de2ac9a 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Defolt data SIM üçün alt Id:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL Buraxılışı (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL Buraxılışı (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Xananın məkan məlumatı (ləğv edilib):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE Fiziki Kanal Konfiqurasiyası:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Mobil məlumatın yenilənmə göstəricisi:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Operatorun bütün mobil ölçü məlumatı:"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 8940af5..95c5b70 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -118,7 +118,7 @@
<string name="sum_cfnry_enabled" msgid="3000500837493854799">"Prosleđuje se na <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
<string name="sum_cfnry_disabled" msgid="1990563512406017880">"Isključeno"</string>
<string name="disable_cfnry_forbidden" msgid="3174731413216550689">"Operater ne podržava onemogućavanje preusmeravanja poziva kada se na poziv ne odgovori."</string>
- <string name="labelCFNRc" msgid="4163399350778066013">"Kad sam nedostupan/a"</string>
+ <string name="labelCFNRc" msgid="4163399350778066013">"Kad sam nedostupan/na"</string>
<string name="messageCFNRc" msgid="6980340731313007250">"Broj kad je nedostupno"</string>
<string name="sum_cfnrc_enabled" msgid="1799069234006073477">"Prosleđuje se na <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
<string name="sum_cfnrc_disabled" msgid="739289696796917683">"Isključeno"</string>
@@ -863,7 +863,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SubID podrazumevanog SIM-a za podatke:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL propusni opseg (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL propusni opseg (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Informacije o lokaciji predajnika (zastarelo):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Konfiguracija LTE fizičkog kanala:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Učestalost osvežavanja informacija o predajniku:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Sve informacije o merenju za predajnik:"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 3a3d43d..dd2d14d 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Идентификационен подномер на SIM картата за данни по подразбиране:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Пропускателна способност при изтегляне (кб/сек):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Пропускателна способност при качване (кб/сек):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Информация за местоположението на клетката (оттеглено):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Конфигурация на физическия канал на LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Честота на опресняване на информацията за клетките:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Цялата измервателна информация за клетките:"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index db4be42..7150bce 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"ডিফল্ট ডেটা সিমের SubId:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"ডিএল ব্যান্ডউইথ (কেবিপিএস):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"ইউএল ব্যান্ডউইথ (কেবিপিএস):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"সেল লোকেশন তথ্য (বন্ধ করা হয়েছে):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE ফিজিক্যাল চ্যানেল কনফিগারেশন:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"সেল তথ্যে রিফ্রেশ রেট:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"সমস্ত সেল পরিমাপ তথ্য:"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 7256c32..0df16ec 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -105,7 +105,7 @@
<string name="labelCFU" msgid="8870170873036279706">"Uvijek proslijedi"</string>
<string name="messageCFU" msgid="1361806450979589744">"Uvijek koristi ovaj broj"</string>
<string name="sum_cfu_enabled_indicator" msgid="9030139213402432776">"Prosljeđivanje svih poziva"</string>
- <string name="sum_cfu_enabled" msgid="5806923046528144526">"Preusmjeravaju se svi pozivi na <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
+ <string name="sum_cfu_enabled" msgid="5806923046528144526">"Prosljeđivanje svih poziva na <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
<string name="sum_cfu_enabled_no_number" msgid="7287752761743377930">"Broj je nedostupan"</string>
<string name="sum_cfu_disabled" msgid="5010617134210809853">"Isključeno"</string>
<string name="labelCFB" msgid="615265213360512768">"Kada je zauzeto"</string>
@@ -127,8 +127,8 @@
<string name="call_settings_admin_user_only" msgid="7238947387649986286">"Postavke poziva može promijeniti samo administrator."</string>
<string name="call_settings_with_label" msgid="8460230435361579511">"Postavke (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="error_updating_title" msgid="2024290892676808965">"Greška u postavkama poziva"</string>
- <string name="reading_settings" msgid="1605904432450871183">"Čitanje postavki u toku…"</string>
- <string name="updating_settings" msgid="3650396734816028808">"Ažuriranje postavki u toku…"</string>
+ <string name="reading_settings" msgid="1605904432450871183">"Čitanje postavki…"</string>
+ <string name="updating_settings" msgid="3650396734816028808">"Ažuriranje postavki…"</string>
<string name="reverting_settings" msgid="7378668837291012205">"Vraćanje postavki u toku…"</string>
<string name="response_error" msgid="3904481964024543330">"Neočekivani odgovor mreže."</string>
<string name="exception_error" msgid="330994460090467">"Greška na mreži ili SIM kartici."</string>
@@ -604,7 +604,7 @@
<string name="ota_hfa_activation_title" msgid="3300556778212729671">"Aktivacija u toku..."</string>
<string name="ota_hfa_activation_dialog_message" msgid="7921718445773342996">"Telefon aktivira uslugu prijenosa mobilnih podataka.\n\nTo može potrajati do 5 minuta."</string>
<string name="ota_skip_activation_dialog_title" msgid="7666611236789203797">"Preskočiti aktivaciju?"</string>
- <string name="ota_skip_activation_dialog_message" msgid="6691722887019708713">"Ako preskočite aktivaciju, nećete moći upućivati pozive niti se povezati na mobilne podatkovne mreže (iako se možete povezati na WiFi mreže). Dok ne aktivirate telefon, prikazivat će se upit za aktivaciju svaki put kada upalite telefon."</string>
+ <string name="ota_skip_activation_dialog_message" msgid="6691722887019708713">"Ako preskočite aktivaciju, nećete moći upućivati pozive niti se povezati na mobilne podatkovne mreže (iako se možete povezati s WiFi mrežama). Dok ne aktivirate telefon, prikazivat će se upit za aktivaciju svaki put kada upalite telefon."</string>
<string name="ota_skip_activation_dialog_skip_label" msgid="5908029466817825633">"Preskoči"</string>
<string name="ota_activate" msgid="7939695753665438357">"Aktiviraj"</string>
<string name="ota_title_activate_success" msgid="1272135024761004889">"Telefon je aktiviran."</string>
@@ -863,7 +863,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Pomoćni ID za zadani podatkovni SIM:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL propusnost (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL propusnost (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Informacije o lokaciji ćelije (zastarjele):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Konfiguracija LTE fizičkog kanala:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Interval osvježavanja informacija o ćeliji:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Sve informacije o mjerenju ćelije:"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index a99de70..ebec696 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -62,7 +62,7 @@
<string name="labelCdmaMore_with_label" msgid="7759692829160238152">"Configuració de trucades CDMA (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="apn_settings" msgid="1978652203074756623">"Noms dels punts d\'accés"</string>
<string name="settings_label" msgid="9101778088412567956">"Configuració de xarxa"</string>
- <string name="phone_accounts" msgid="1216879437523774604">"Comptes de trucades"</string>
+ <string name="phone_accounts" msgid="1216879437523774604">"Comptes de trucada"</string>
<string name="phone_accounts_make_calls_with" msgid="16747814788918145">"Truca amb"</string>
<string name="phone_accounts_make_sip_calls_with" msgid="4691221006731847255">"Fes trucades SIP amb"</string>
<string name="phone_accounts_ask_every_time" msgid="6192347582666047168">"Pregunta primer"</string>
@@ -95,7 +95,7 @@
<string name="sum_loading_settings" msgid="434063780286688775">"Carregant la configuració..."</string>
<string name="sum_hide_caller_id" msgid="131100328602371933">"Número amagat en trucades sortints"</string>
<string name="sum_show_caller_id" msgid="3571854755324664591">"Número mostrat a les trucades de sortida"</string>
- <string name="sum_default_caller_id" msgid="1767070797135682959">"Utilitza la configuració predeterminada de l\'operador per mostrar el meu número a les trucades de sortida"</string>
+ <string name="sum_default_caller_id" msgid="1767070797135682959">"Utilitza la configuració predeterminada de l\'operador per mostrar el meu número a les trucades sortints"</string>
<string name="labelCW" msgid="8449327023861428622">"Trucada en espera"</string>
<string name="sum_cw_enabled" msgid="3977308526187139996">"Durant una trucada, avisa\'m de les trucades entrants"</string>
<string name="sum_cw_disabled" msgid="3658094589461768637">"Durant una trucada, avisa\'m de les trucades entrants"</string>
@@ -583,7 +583,7 @@
<string name="hac_mode_summary" msgid="7774989500136009881">"Activa la compatibilitat amb audiòfons"</string>
<string name="rtt_mode_title" msgid="3075948111362818043">"Trucada de text en temps real (RTT)"</string>
<string name="rtt_mode_summary" msgid="8631541375609989562">"Permet l\'ús de missatges en una trucada de veu"</string>
- <string name="rtt_mode_more_information" msgid="587500128658756318">"La funció RTT ofereix assistència als usuaris que són sords, tenen problemes d\'audició o alteracions de la parla, o bé que necessiten un text a banda de la veu.<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>Més informació</a>\n <br><br> - Les trucades RTT es desen en forma de transcripció.\n <br> - Aquesta funció no està disponible per a videotrucades."</string>
+ <string name="rtt_mode_more_information" msgid="587500128658756318">"La funció RTT ofereix assistència als usuaris que són sords, tenen deficiències auditives o alteracions de la parla, o bé que necessiten un text a banda de la veu.<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>Més informació</a>\n <br><br> - Les trucades RTT es desen en forma de transcripció.\n <br> - Aquesta funció no està disponible per a videotrucades."</string>
<string name="no_rtt_when_roaming" msgid="5268008247378355389">"Nota: la funció RTT no està disponible en itinerància"</string>
<string-array name="tty_mode_entries">
<item msgid="3238070884803849303">"TTY desactivat"</item>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Subidentificador de la SIM de dades predeterminada:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Amplada de banda de baixada (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Amplada de banda de pujada (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Informació d\'ubicació del mòbil (obsoleta):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Configuració del canal físic de LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Freqüència d\'actualització de la informació del mòbil:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Tota la informació de mesures del mòbil:"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 7ce0a25..a13d561 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -141,7 +141,7 @@
<string name="close_dialog" msgid="1074977476136119408">"OK"</string>
<string name="enable" msgid="2636552299455477603">"Zapnout"</string>
<string name="disable" msgid="1122698860799462116">"Vypnout"</string>
- <string name="change_num" msgid="6982164494063109334">"Aktualiz."</string>
+ <string name="change_num" msgid="6982164494063109334">"Aktualizovat"</string>
<string-array name="clir_display_values">
<item msgid="8477364191403806960">"Výchozí nastavení sítě"</item>
<item msgid="6813323051965618926">"Skrýt číslo"</item>
@@ -865,7 +865,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SubId výchozí datové SIM karty:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Rychlost stahování (kB/s):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Rychlost nahrávání (kB/s):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Informace o poloze mobilní sítě (podpora ukončena):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Konfigurace fyzického kanálu LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Interval obnovení informací o mobilní síti:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Všechny údaje o měření mobilní sítě:"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index c8a1c34..5fc0066 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -572,7 +572,7 @@
<string name="onscreenMergeCallsText" msgid="3692389519611225407">"Slå opkald sammen"</string>
<string name="onscreenSwapCallsText" msgid="2682542150803377991">"Skift"</string>
<string name="onscreenManageCallsText" msgid="1162047856081836469">"Administrer opkald"</string>
- <string name="onscreenManageConferenceText" msgid="4700574060601755137">"Admin. konference"</string>
+ <string name="onscreenManageConferenceText" msgid="4700574060601755137">"Admin. møde"</string>
<string name="onscreenAudioText" msgid="7224226735052019986">"Lyd"</string>
<string name="onscreenVideoCallText" msgid="1743992456126258698">"Videoopkald"</string>
<string name="importSimEntry" msgid="3892354284082689894">"Importer"</string>
@@ -800,7 +800,7 @@
<string name="supp_service_additional_call_forwarded" msgid="8772753260008398632">"Det andet opkald er videresendt."</string>
<string name="supp_service_additional_ect_connected" msgid="8525934162945220237">"Opkaldet blev tilsluttet ECT."</string>
<string name="supp_service_additional_ect_connecting" msgid="7046240728781222753">"Opkaldet tilsluttes ECT."</string>
- <string name="supp_service_call_on_hold" msgid="2836811319594503059">"Parkeret opkald."</string>
+ <string name="supp_service_call_on_hold" msgid="2836811319594503059">"Opkald på hold."</string>
<string name="supp_service_call_resumed" msgid="3786864005920743546">"Opkaldet er genoptaget."</string>
<string name="supp_service_deflected_call" msgid="7565979024562921707">"Opkaldet blev viderestillet."</string>
<string name="supp_service_forwarded_call" msgid="6475776013771821457">"Videresendt opkald."</string>
@@ -863,7 +863,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Under-id for standard-SIM-kort til data:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Båndbredde til download (kB/sek.):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Båndbredde til upload (kB/sek.):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Oplysning om placering af celle (udfaset):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Fysisk kanalkonfiguration for LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Opdateringsfrekvens for celleoplysninger:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Alle oplysninger om cellemåling:"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index ac9859c..84353f4 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -524,7 +524,7 @@
<string name="notification_voicemail_no_vm_number" msgid="3423686009815186750">"Mailboxnummer unbekannt"</string>
<string name="notification_network_selection_title" msgid="255595526707809121">"kein Dienst"</string>
<string name="notification_network_selection_text" msgid="553288408722427659">"Ausgewähltes Netzwerk nicht verfügbar:<xliff:g id="OPERATOR_NAME">%s</xliff:g>"</string>
- <string name="incall_error_power_off" product="watch" msgid="7191184639454113633">"Zum Anrufen Mobilfunknetz aktivieren, Flugmodus oder Energiesparmodus deaktivieren."</string>
+ <string name="incall_error_power_off" product="watch" msgid="7191184639454113633">"Zum Anrufen Mobilfunknetz aktivieren, Flugmodus oder Stromsparmodus deaktivieren."</string>
<string name="incall_error_power_off" product="default" msgid="8131672264311208673">"Deaktiviere zum Telefonieren den Flugmodus."</string>
<string name="incall_error_power_off_wfc" msgid="9125661184694727052">"Deaktiviere zum Telefonieren den Flugmodus oder stelle eine WLAN-Verbindung her."</string>
<string name="incall_error_ecm_emergency_only" msgid="5622379058883722080">"Beende den Notfallrückrufmodus, um einen Anruf zu tätigen, bei dem es sich nicht um einen Notfall handelt."</string>
@@ -863,7 +863,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Unter-ID der standardmäßigen Daten-SIM:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL-Bandbreite (kbit/s):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL-Bandbreite (kbit/s):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Standortinformationen des Mobiltelefons (eingestellt):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Konfiguration des physischen LTE-Kanals:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Aktualisierungsrate der Mobiltelefoninformationen:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Alle Informationen zu Zellenmesswerten:"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index b732508..20d14e8 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -74,7 +74,7 @@
<string name="phone_accounts_configure_account_settings" msgid="6622119715253196586">"Configure account settings"</string>
<string name="phone_accounts_all_calling_accounts" msgid="1609600743500618823">"All calling accounts"</string>
<string name="phone_accounts_all_calling_accounts_summary" msgid="2214134955430107240">"Select which accounts can make calls"</string>
- <string name="wifi_calling" msgid="3650509202851355742">"Wi-Fi Calling"</string>
+ <string name="wifi_calling" msgid="3650509202851355742">"Wi-Fi calling"</string>
<string name="connection_service_default_label" msgid="7332739049855715584">"Built-in connection service"</string>
<string name="voicemail" msgid="7697769412804195032">"Voicemail"</string>
<string name="voicemail_settings_with_label" msgid="4228431668214894138">"Voicemail (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SubId of default data SIM:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL bandwidth (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL bandwidth (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Mobile location info (deprecated):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE physical channel configuration:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Mobile info refresh rate:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"All mobile measurement info:"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 4cd63be..9daca55 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -74,7 +74,7 @@
<string name="phone_accounts_configure_account_settings" msgid="6622119715253196586">"Configure account settings"</string>
<string name="phone_accounts_all_calling_accounts" msgid="1609600743500618823">"All calling accounts"</string>
<string name="phone_accounts_all_calling_accounts_summary" msgid="2214134955430107240">"Select which accounts can make calls"</string>
- <string name="wifi_calling" msgid="3650509202851355742">"Wi-Fi Calling"</string>
+ <string name="wifi_calling" msgid="3650509202851355742">"Wi-Fi calling"</string>
<string name="connection_service_default_label" msgid="7332739049855715584">"Built-in connection service"</string>
<string name="voicemail" msgid="7697769412804195032">"Voicemail"</string>
<string name="voicemail_settings_with_label" msgid="4228431668214894138">"Voicemail (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
@@ -471,7 +471,7 @@
<string name="simContacts_emptyLoading" msgid="4989040293858675483">"Reading from SIM card…"</string>
<string name="simContacts_empty" msgid="1135632055473689521">"No contacts on your SIM card."</string>
<string name="simContacts_title" msgid="2714029230160136647">"Select contacts to import"</string>
- <string name="simContacts_airplaneMode" msgid="4654884030631503808">"Turn off aeroplane mode to import contacts from the SIM card."</string>
+ <string name="simContacts_airplaneMode" msgid="4654884030631503808">"Turn off Airplane mode to import contacts from the SIM card."</string>
<string name="enable_pin" msgid="967674051730845376">"Enable/disable SIM PIN"</string>
<string name="change_pin" msgid="3657869530942905790">"Change SIM PIN"</string>
<string name="enter_pin_text" msgid="3182311451978663356">"SIM PIN:"</string>
@@ -523,8 +523,8 @@
<string name="notification_network_selection_title" msgid="255595526707809121">"No service"</string>
<string name="notification_network_selection_text" msgid="553288408722427659">"Selected network<xliff:g id="OPERATOR_NAME">%s</xliff:g> unavailable"</string>
<string name="incall_error_power_off" product="watch" msgid="7191184639454113633">"Turn on mobile network, turn off airplane mode or turn off battery saver mode to make a call."</string>
- <string name="incall_error_power_off" product="default" msgid="8131672264311208673">"Turn off aeroplane mode to make a call."</string>
- <string name="incall_error_power_off_wfc" msgid="9125661184694727052">"Turn off aeroplane mode or connect to a wireless network to make a call."</string>
+ <string name="incall_error_power_off" product="default" msgid="8131672264311208673">"Turn off Airplane mode to make a call."</string>
+ <string name="incall_error_power_off_wfc" msgid="9125661184694727052">"Turn off Airplane mode or connect to a wireless network to make a call."</string>
<string name="incall_error_ecm_emergency_only" msgid="5622379058883722080">"Exit emergency callback mode to make a non-emergency call."</string>
<string name="incall_error_emergency_only" msgid="8786127461027964653">"Not registered on network."</string>
<string name="incall_error_out_of_service" msgid="1927265196942672791">"Mobile network not available."</string>
@@ -552,7 +552,7 @@
<string name="emergency_call_shortcut_hint" msgid="1290485125107779500">"Tap again to call <xliff:g id="EMERGENCY_NUMBER">%s</xliff:g>"</string>
<string name="emergency_enable_radio_dialog_message" msgid="1695305158151408629">"Turning on radio…"</string>
<string name="emergency_enable_radio_dialog_retry" msgid="4329131876852608587">"No service. Trying again…"</string>
- <string name="radio_off_during_emergency_call" msgid="8011154134040481609">"Cannot enter aeroplane mode during an emergency call."</string>
+ <string name="radio_off_during_emergency_call" msgid="8011154134040481609">"Cannot enter Airplane mode during an emergency call."</string>
<string name="dial_emergency_error" msgid="825822413209026039">"Can\'t call. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> is not an emergency number."</string>
<string name="dial_emergency_empty_error" msgid="2785803395047793634">"Can\'t call. Dial an emergency number."</string>
<string name="dial_emergency_calling_not_available" msgid="6485846193794727823">"Emergency calling not available"</string>
@@ -701,7 +701,7 @@
<string name="mobile_data_activate_button" msgid="1139792516354374612">"ADD DATA"</string>
<string name="mobile_data_activate_cancel_button" msgid="3530174817572005860">"CANCEL"</string>
<string name="clh_card_title_call_ended_txt" msgid="5977978317527299698">"Call ended"</string>
- <string name="clh_callFailed_powerOff_txt" msgid="8279934912560765361">"Aeroplane mode is on"</string>
+ <string name="clh_callFailed_powerOff_txt" msgid="8279934912560765361">"Airplane mode is on"</string>
<string name="clh_callFailed_simError_txt" msgid="5128538525762326413">"Can\'t access SIM card"</string>
<string name="clh_incall_error_out_of_service_txt" msgid="2736010617446749869">"Mobile network not available"</string>
<string name="clh_callFailed_unassigned_number_txt" msgid="141967660286695682">"Issue with phone number you are trying to dial. Error code 1."</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SubId of default data SIM:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL bandwidth (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL bandwidth (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Mobile location info (deprecated):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE physical channel configuration:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Mobile info refresh rate:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"All mobile measurement info:"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index b732508..20d14e8 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -74,7 +74,7 @@
<string name="phone_accounts_configure_account_settings" msgid="6622119715253196586">"Configure account settings"</string>
<string name="phone_accounts_all_calling_accounts" msgid="1609600743500618823">"All calling accounts"</string>
<string name="phone_accounts_all_calling_accounts_summary" msgid="2214134955430107240">"Select which accounts can make calls"</string>
- <string name="wifi_calling" msgid="3650509202851355742">"Wi-Fi Calling"</string>
+ <string name="wifi_calling" msgid="3650509202851355742">"Wi-Fi calling"</string>
<string name="connection_service_default_label" msgid="7332739049855715584">"Built-in connection service"</string>
<string name="voicemail" msgid="7697769412804195032">"Voicemail"</string>
<string name="voicemail_settings_with_label" msgid="4228431668214894138">"Voicemail (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SubId of default data SIM:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL bandwidth (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL bandwidth (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Mobile location info (deprecated):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE physical channel configuration:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Mobile info refresh rate:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"All mobile measurement info:"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index b732508..20d14e8 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -74,7 +74,7 @@
<string name="phone_accounts_configure_account_settings" msgid="6622119715253196586">"Configure account settings"</string>
<string name="phone_accounts_all_calling_accounts" msgid="1609600743500618823">"All calling accounts"</string>
<string name="phone_accounts_all_calling_accounts_summary" msgid="2214134955430107240">"Select which accounts can make calls"</string>
- <string name="wifi_calling" msgid="3650509202851355742">"Wi-Fi Calling"</string>
+ <string name="wifi_calling" msgid="3650509202851355742">"Wi-Fi calling"</string>
<string name="connection_service_default_label" msgid="7332739049855715584">"Built-in connection service"</string>
<string name="voicemail" msgid="7697769412804195032">"Voicemail"</string>
<string name="voicemail_settings_with_label" msgid="4228431668214894138">"Voicemail (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SubId of default data SIM:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL bandwidth (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL bandwidth (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Mobile location info (deprecated):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE physical channel configuration:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Mobile info refresh rate:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"All mobile measurement info:"</string>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index 3f04696..66e99d2 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SubId of default data SIM:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL Bandwidth (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL Bandwidth (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Cell Location Info (deprecated):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE Physical Channel Configuration:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Cell Info Refresh Rate:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"All Cell Measurement Info:"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index e8a92c4..0d12fd5 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -43,7 +43,7 @@
<string name="send_button" msgid="5070379600779031932">"Enviar"</string>
<string name="pause_prompt_yes" msgid="8184132073048369575">"Sí"</string>
<string name="pause_prompt_no" msgid="2145264674774138579">"No"</string>
- <string name="wild_prompt_str" msgid="5858910969703305375">"Sustituir el carácter comodín por"</string>
+ <string name="wild_prompt_str" msgid="5858910969703305375">"Reemplazar el carácter comodín por"</string>
<string name="no_vm_number" msgid="6623853880546176930">"Falta el número del buzón de voz."</string>
<string name="no_vm_number_msg" msgid="5165161462411372504">"No se ha almacenado ningún número de buzón de voz en la tarjeta SIM."</string>
<string name="add_vm_number_str" msgid="7368168964435881637">"Añadir número"</string>
@@ -62,7 +62,7 @@
<string name="labelCdmaMore_with_label" msgid="7759692829160238152">"Ajustes de llamada de CDMA (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="apn_settings" msgid="1978652203074756623">"Nombres de puntos de acceso"</string>
<string name="settings_label" msgid="9101778088412567956">"Ajustes de red"</string>
- <string name="phone_accounts" msgid="1216879437523774604">"Cuentas de llamadas"</string>
+ <string name="phone_accounts" msgid="1216879437523774604">"Cuentas de llamada"</string>
<string name="phone_accounts_make_calls_with" msgid="16747814788918145">"Realizar llamadas con"</string>
<string name="phone_accounts_make_sip_calls_with" msgid="4691221006731847255">"Realizar llamadas SIP con"</string>
<string name="phone_accounts_ask_every_time" msgid="6192347582666047168">"Preguntar primero"</string>
@@ -184,7 +184,7 @@
<string name="forbidden_network" msgid="5081729819561333023">"(prohibida)"</string>
<string name="choose_network_title" msgid="5335832663422653082">"Elegir red"</string>
<string name="network_disconnected" msgid="8844141106841160825">"Desconectado"</string>
- <string name="network_connected" msgid="2760235679963580224">"Conectada"</string>
+ <string name="network_connected" msgid="2760235679963580224">"Conectado"</string>
<string name="network_connecting" msgid="160901383582774987">"Conectando..."</string>
<string name="network_could_not_connect" msgid="6547460848093727998">"No se ha podido conectar"</string>
<string-array name="preferred_network_mode_choices">
@@ -863,7 +863,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"ID secundario de la SIM de datos predeterminada:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Ancho de banda de bajada (Kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Ancho de banda de subida (Kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Información sobre la ubicación del teléfono (obsoleto):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Configuración del canal físico de LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Frecuencia de actualización de la información del teléfono:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Información sobre las dimensiones de los teléfonos:"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index dcbbc23..fb7801f 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Andmete vaike-SIM-kaardi alam-ID:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL-i ribalaius (kbit/s):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL-i ribalaius (kbit/s):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Kärje asukohateave (tugi on katkestatud):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE füüsilise kanali konfiguratsioon:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Kärje teabe värskendamissagedus:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Kõik kärje mõõteandmed:"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 1c75c30..250b072 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -74,7 +74,7 @@
<string name="phone_accounts_configure_account_settings" msgid="6622119715253196586">"Konfiguratu kontuaren ezarpenak"</string>
<string name="phone_accounts_all_calling_accounts" msgid="1609600743500618823">"Deiak egiteko kontu guztiak"</string>
<string name="phone_accounts_all_calling_accounts_summary" msgid="2214134955430107240">"Hautatu zein kontutatik egin daitezkeen deiak"</string>
- <string name="wifi_calling" msgid="3650509202851355742">"Wi-Fi bidezko deiak"</string>
+ <string name="wifi_calling" msgid="3650509202851355742">"Wifi bidezko deiak"</string>
<string name="connection_service_default_label" msgid="7332739049855715584">"Konektatzeko zerbitzu integratua"</string>
<string name="voicemail" msgid="7697769412804195032">"Erantzungailua"</string>
<string name="voicemail_settings_with_label" msgid="4228431668214894138">"Erantzungailua (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
@@ -95,32 +95,32 @@
<string name="sum_loading_settings" msgid="434063780286688775">"Ezarpenak kargatzen…"</string>
<string name="sum_hide_caller_id" msgid="131100328602371933">"Ezkutatu zenbakia irteerako deietan"</string>
<string name="sum_show_caller_id" msgid="3571854755324664591">"Erakutsi zenbakia irteerako deietan"</string>
- <string name="sum_default_caller_id" msgid="1767070797135682959">"Erabili operadorearen ezarpen lehenetsiak irteerako deietan nire zenbakia erakusteko"</string>
+ <string name="sum_default_caller_id" msgid="1767070797135682959">"Erabili operadorearen ezarpen lehenetsiak, egiten ditudan deietan nire zenbakia erakusteko"</string>
<string name="labelCW" msgid="8449327023861428622">"Deiak zain"</string>
- <string name="sum_cw_enabled" msgid="3977308526187139996">"Deiak abian dauden bitartean, eman sarrerako deien berri"</string>
- <string name="sum_cw_disabled" msgid="3658094589461768637">"Deiak abian dauden bitartean, eman sarrerako deien berri"</string>
+ <string name="sum_cw_enabled" msgid="3977308526187139996">"Deiak abian dauden bitartean, eman jasotzen ditudan deien berri"</string>
+ <string name="sum_cw_disabled" msgid="3658094589461768637">"Deiak abian dauden bitartean, eman jasotzen ditudan deien berri"</string>
<string name="call_forwarding_settings" msgid="8937130467468257671">"Dei-desbideratzearen ezarpenak"</string>
<string name="call_forwarding_settings_with_label" msgid="2345432813399564272">"Deiak desbideratzeko ezarpenak (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="labelCF" msgid="3578719437928476078">"Dei-desbideratzea"</string>
<string name="labelCFU" msgid="8870170873036279706">"Desbideratu beti"</string>
<string name="messageCFU" msgid="1361806450979589744">"Erabili beti zenbaki hau"</string>
<string name="sum_cfu_enabled_indicator" msgid="9030139213402432776">"Dei guztiak desbideratzen"</string>
- <string name="sum_cfu_enabled" msgid="5806923046528144526">"Dei guztiak <xliff:g id="PHONENUMBER">{0}</xliff:g> zenbakira desbideratzen"</string>
+ <string name="sum_cfu_enabled" msgid="5806923046528144526">"Dei guztiak <xliff:g id="PHONENUMBER">{0}</xliff:g> zenbakira desbideratzen dira"</string>
<string name="sum_cfu_enabled_no_number" msgid="7287752761743377930">"Zenbakia ez dago eskuragarri"</string>
<string name="sum_cfu_disabled" msgid="5010617134210809853">"Desaktibatuta"</string>
<string name="labelCFB" msgid="615265213360512768">"Okupatuta nagoenean"</string>
<string name="messageCFB" msgid="1958017270393563388">"Okupatuta dagoenerako zenbakia"</string>
- <string name="sum_cfb_enabled" msgid="332037613072049492">"<xliff:g id="PHONENUMBER">{0}</xliff:g> zenbakira desbideratzen"</string>
+ <string name="sum_cfb_enabled" msgid="332037613072049492">"<xliff:g id="PHONENUMBER">{0}</xliff:g> zenbakira desbideratzen dira"</string>
<string name="sum_cfb_disabled" msgid="3589913334164866035">"Desaktibatuta"</string>
<string name="disable_cfb_forbidden" msgid="4831494744351633961">"Operadoreak ez du dei-desbideratzeak desgaitzea onartzen telefonoa okupatuta dagoen bitartean."</string>
<string name="labelCFNRy" msgid="3403533792248457946">"Erantzuten ez dudanean"</string>
<string name="messageCFNRy" msgid="7644434155765359009">"Erantzunik jasotzen ez denerako zenbakia"</string>
- <string name="sum_cfnry_enabled" msgid="3000500837493854799">"<xliff:g id="PHONENUMBER">{0}</xliff:g> zenbakira desbideratzen"</string>
+ <string name="sum_cfnry_enabled" msgid="3000500837493854799">"<xliff:g id="PHONENUMBER">{0}</xliff:g> zenbakira desbideratzen dira"</string>
<string name="sum_cfnry_disabled" msgid="1990563512406017880">"Desaktibatuta"</string>
<string name="disable_cfnry_forbidden" msgid="3174731413216550689">"Operadoreak ez du dei-desbideratzeak desgaitzea onartzen telefonoak erantzuten ez duenean."</string>
- <string name="labelCFNRc" msgid="4163399350778066013">"Konektatu gabe nagoenean"</string>
+ <string name="labelCFNRc" msgid="4163399350778066013">"Telefonoa itzalita edo estalduratik at dagoenean"</string>
<string name="messageCFNRc" msgid="6980340731313007250">"Eskuragarri ez dagoenerako zenbakia"</string>
- <string name="sum_cfnrc_enabled" msgid="1799069234006073477">"<xliff:g id="PHONENUMBER">{0}</xliff:g> zenbakira desbideratzen"</string>
+ <string name="sum_cfnrc_enabled" msgid="1799069234006073477">"<xliff:g id="PHONENUMBER">{0}</xliff:g> zenbakira desbideratzen dira"</string>
<string name="sum_cfnrc_disabled" msgid="739289696796917683">"Desaktibatuta"</string>
<string name="disable_cfnrc_forbidden" msgid="775348748084726890">"Operadoreak ez du dei-desbideratzeak desgaitzea onartzen telefonoa eskuragarri ez dagoen bitartean."</string>
<string name="updating_title" msgid="6130548922615719689">"Deien ezarpenak"</string>
@@ -298,7 +298,7 @@
<string name="sim_selection_required_pref" msgid="6985901872978341314">"Hautatu egin behar da"</string>
<string name="sim_change_data_title" msgid="9142726786345906606">"Datuetarako SIMa aldatu nahi duzu?"</string>
<string name="sim_change_data_message" msgid="3567358694255933280">"Datu-konexiorako, <xliff:g id="NEW_SIM">%1$s</xliff:g> txartela erabili nahi duzu <xliff:g id="OLD_SIM">%2$s</xliff:g> txartelaren ordez?"</string>
- <string name="wifi_calling_settings_title" msgid="5800018845662016507">"Wi-Fi bidezko deiak"</string>
+ <string name="wifi_calling_settings_title" msgid="5800018845662016507">"Wifi bidezko deiak"</string>
<string name="video_calling_settings_title" msgid="342829454913266078">"Operadorearen bideo-deiak"</string>
<string name="gsm_umts_options" msgid="4968446771519376808">"GSM/UMTS aukerak"</string>
<string name="cdma_options" msgid="3669592472226145665">"CDMA aukerak"</string>
@@ -568,7 +568,7 @@
<string name="onscreenEndCallText" msgid="6138725377654842757">"Amaitu"</string>
<string name="onscreenShowDialpadText" msgid="658465753816164079">"Markagailua"</string>
<string name="onscreenMuteText" msgid="5470306116733843621">"Desaktibatu audioa"</string>
- <string name="onscreenAddCallText" msgid="9075675082903611677">"Gehitu deia"</string>
+ <string name="onscreenAddCallText" msgid="9075675082903611677">"Gehitu dei bat"</string>
<string name="onscreenMergeCallsText" msgid="3692389519611225407">"Bateratu deiak"</string>
<string name="onscreenSwapCallsText" msgid="2682542150803377991">"Trukatu"</string>
<string name="onscreenManageCallsText" msgid="1162047856081836469">"Kudeatu deiak"</string>
@@ -616,7 +616,7 @@
<string name="ota_progress" msgid="8837259285255700132">"Telefonoa programatzen…"</string>
<string name="ota_failure" msgid="5674217489921481576">"Ezin izan da programatu telefonoa"</string>
<string name="ota_successful" msgid="1106825981548107774">"Telefonoa aktibatu egin da. Zerbitzua hasteko 15 minutu ere behar izan daitezke."</string>
- <string name="ota_unsuccessful" msgid="8531037653803955754">"Ez duzu telefonoa aktibatu.\nBaliteke estaldura gehiago duen eremu bat aurkitu behar izatea (leiho baten ondoan edo kanpoan).\n\nSaiatu berriro edo, aukera gehiago ezagutzeko, deitu bezeroarentzako zerbitzura."</string>
+ <string name="ota_unsuccessful" msgid="8531037653803955754">"Ez duzu telefonoa aktibatu.\nBaliteke estaldura gehiago duen eremu bat aurkitu behar izatea (leiho baten ondoan edo kanpoan).\n\nSaiatu berriro edo, aukera gehiago ezagutzeko, deitu bezeroarentzako laguntza-zerbitzura."</string>
<string name="ota_spc_failure" msgid="904092035241370080">"SPC HUTSEGITE GEHIEGI"</string>
<string name="ota_call_end" msgid="8657746378290737034">"Atzera"</string>
<string name="ota_try_again" msgid="6914781945599998550">"Saiatu berriro"</string>
@@ -863,7 +863,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Datu-konexioetarako SIM lehenetsiaren azpiIDa:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Deskargatzeko banda-zabalera (Kb/s):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Kargen banda-zabalera (Kb/s):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Sare mugikorraren kokapenaren informazioa (zaharkitua):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE kanal fisikoaren konfigurazioa:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Sare mugikorraren informazioa eguneratzeko maiztasuna:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Sare mugikorraren neurketa guztien informazioa:"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 5e029f0..def3638 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -135,7 +135,7 @@
<string name="stk_cc_ss_to_dial_error" msgid="5147693491690618704">"درخواست SS به تماس معمولی تغییر کرد"</string>
<string name="stk_cc_ss_to_ussd_error" msgid="8330749347425752192">"درخواست SS بهدرخواست USSD تغییر کرد"</string>
<string name="stk_cc_ss_to_ss_error" msgid="8297155544652134278">"بهدرخواست SS جدید تغییر کرد"</string>
- <string name="stk_cc_ss_to_dial_video_error" msgid="4255261231466032505">"درخواست SS به تماس ویدیویی تغییر کرد"</string>
+ <string name="stk_cc_ss_to_dial_video_error" msgid="4255261231466032505">"درخواست SS به تماس تصویری تغییر کرد"</string>
<string name="fdn_check_failure" msgid="1833769746374185247">"تنظیم اعداد شمارهگیری ثابت برنامههای تلفن شما غیرفعال است. درنتیجه، برخی از ویژگیهای مربوط به تماس کار نمیکند."</string>
<string name="radio_off_error" msgid="8321564164914232181">"قبل از مشاهدهٔ این تنظیمات، رادیو را روشن کنید."</string>
<string name="close_dialog" msgid="1074977476136119408">"تأیید"</string>
@@ -299,7 +299,7 @@
<string name="sim_change_data_title" msgid="9142726786345906606">"سیمکارت داده تغییر کند؟"</string>
<string name="sim_change_data_message" msgid="3567358694255933280">"برای داده تلفن همراه، از <xliff:g id="NEW_SIM">%1$s</xliff:g> بهجای <xliff:g id="OLD_SIM">%2$s</xliff:g> استفاده شود؟"</string>
<string name="wifi_calling_settings_title" msgid="5800018845662016507">"تماس ازطریق Wi-Fi"</string>
- <string name="video_calling_settings_title" msgid="342829454913266078">"تماس ویدیویی با شرکت مخابراتی"</string>
+ <string name="video_calling_settings_title" msgid="342829454913266078">"تماس تصویری با شرکت مخابراتی"</string>
<string name="gsm_umts_options" msgid="4968446771519376808">"گزینههای GSM/UMTS"</string>
<string name="cdma_options" msgid="3669592472226145665">"گزینههای CDMA"</string>
<string name="throttle_data_usage" msgid="1944145350660420711">"مصرف داده"</string>
@@ -446,7 +446,7 @@
<string name="tty_mode_option_summary" msgid="4770510287236494371">"تنظیم حالت TTY"</string>
<string name="auto_retry_mode_title" msgid="2985801935424422340">"سعی مجدد خودکار"</string>
<string name="auto_retry_mode_summary" msgid="2863919925349511402">"فعال کردن حالت سعی مجدد خودکار"</string>
- <string name="tty_mode_not_allowed_video_call" msgid="6551976083652752815">"تغییر حالت TTY در طول تماس ویدئویی مجاز نیست"</string>
+ <string name="tty_mode_not_allowed_video_call" msgid="6551976083652752815">"تغییر حالت TTY در طول تماس تصویری مجاز نیست"</string>
<string name="menu_add" msgid="5616487894975773141">"افزودن مخاطب"</string>
<string name="menu_edit" msgid="3593856941552460706">"ویرایش مخاطب"</string>
<string name="menu_delete" msgid="6326861853830546488">"حذف مخاطب"</string>
@@ -572,7 +572,7 @@
<string name="onscreenManageCallsText" msgid="1162047856081836469">"مدیریت تماسها"</string>
<string name="onscreenManageConferenceText" msgid="4700574060601755137">"مدیریت کنفرانس"</string>
<string name="onscreenAudioText" msgid="7224226735052019986">"صوتی"</string>
- <string name="onscreenVideoCallText" msgid="1743992456126258698">"تماس ویدئویی"</string>
+ <string name="onscreenVideoCallText" msgid="1743992456126258698">"تماس تصویری"</string>
<string name="importSimEntry" msgid="3892354284082689894">"وارد کردن"</string>
<string name="importAllSimEntries" msgid="2628391505643564007">"وارد کردن همه"</string>
<string name="importingSimContacts" msgid="4995457122107888932">"وارد کردن مخاطبین سیم"</string>
@@ -654,8 +654,8 @@
<string name="voicemail_change_pin_dialog_title" msgid="4633077715231764435">"تغییر پین"</string>
<string name="preference_category_ringtone" msgid="8787281191375434976">"آهنگ زنگ و لرزش"</string>
<string name="pstn_connection_service_label" msgid="9200102709997537069">"سیمکارتهای داخلی"</string>
- <string name="enable_video_calling_title" msgid="7246600931634161830">"روشن کردن تماس ویدئویی"</string>
- <string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"برای روشن کردن تماس ویدئویی، باید حالت پیشرفته 4G LTE را در تنظیمات شبکه فعال کنید."</string>
+ <string name="enable_video_calling_title" msgid="7246600931634161830">"روشن کردن تماس تصویری"</string>
+ <string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"برای روشن کردن تماس تصویری، باید حالت پیشرفته 4G LTE را در تنظیمات شبکه فعال کنید."</string>
<string name="enable_video_calling_dialog_settings" msgid="8697890611305307110">"تنظیمات شبکه"</string>
<string name="enable_video_calling_dialog_close" msgid="4298929725917045270">"بستن"</string>
<string name="sim_label_emergency_calls" msgid="9078241989421522310">"تماسهای اضطراری"</string>
@@ -671,7 +671,7 @@
<string name="callFailed_imei_not_accepted" msgid="7257903653685147251">"مرتبط نشد، لطفاً سیم کارت معتبری را وارد کنید."</string>
<string name="callFailed_wifi_lost" msgid="1788036730589163141">"اتصال وایفای قطع شد. تماس پایان یافت."</string>
<string name="dialFailed_low_battery" msgid="6857904237423407056">"به دلیل شارژ کم باتری، نمیتوان تماسی برقرار کرد."</string>
- <string name="callFailed_low_battery" msgid="4056828320214416182">"تماس ویدیویی بهدلیل شارژ کم باتری پایان یافت."</string>
+ <string name="callFailed_low_battery" msgid="4056828320214416182">"تماس تصویری بهدلیل شارژ کم باتری پایان یافت."</string>
<string name="callFailed_emergency_call_over_wfc_not_available" msgid="5944309590693432042">"در این مکان تماس اضطراری با «تماس ازطریق Wi-Fi» امکانپذیر نیست."</string>
<string name="callFailed_wfc_service_not_available_in_this_location" msgid="3624536608369524988">"در این مکان تماس ازطریق Wi-Fi امکانپذیر نیست."</string>
<string name="change_pin_title" msgid="3564254326626797321">"تغییر پین پست صوتی"</string>
@@ -836,7 +836,7 @@
<string name="radio_info_ims_reg_status_not_registered" msgid="8045821447288876085">"ثبتنشده"</string>
<string name="radio_info_ims_feature_status_available" msgid="6493200914756969292">"دردسترس"</string>
<string name="radio_info_ims_feature_status_unavailable" msgid="8930391136839759778">"دردسترس نیست"</string>
- <string name="radio_info_ims_reg_status" msgid="25582845222446390">"ثبت IMS: <xliff:g id="STATUS">%1$s</xliff:g>\nصدا ازطریق LTE: <xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nصدا ازطریق WiFi: <xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\nتماس ویدیویی: <xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nواسط UT: <xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
+ <string name="radio_info_ims_reg_status" msgid="25582845222446390">"ثبت IMS: <xliff:g id="STATUS">%1$s</xliff:g>\nصدا ازطریق LTE: <xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nصدا ازطریق WiFi: <xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\nتماس تصویری: <xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nواسط UT: <xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
<string name="radioInfo_service_in" msgid="45753418231446400">"سرویس دارد"</string>
<string name="radioInfo_service_out" msgid="287972405416142312">"خارج از سرویس"</string>
<string name="radioInfo_service_emergency" msgid="4763879891415016848">"فقط تماسهای اضطراری"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SubId مربوط به سیمکارت داده پیشفرض:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"پهنای باند DL (کیلوبیت بر ثانیه):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"پهنای باند UL (کیلوبیت بر ثانیه):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"اطلاعات مکان شبکه همراه (منسوخ شده):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"پیکربندی کانال فیزیکی LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"نرخ بازخوانی اطلاعات شبکه همراه:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"همه اطلاعات اندازهگیری شبکه همراه:"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index e9ac919..add669c 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -107,21 +107,21 @@
<string name="sum_cfu_enabled_indicator" msgid="9030139213402432776">"Kaikki puhelut siirretään"</string>
<string name="sum_cfu_enabled" msgid="5806923046528144526">"Siirretään kaikki puhelut numeroon <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
<string name="sum_cfu_enabled_no_number" msgid="7287752761743377930">"Numeroon ei saada yhteyttä"</string>
- <string name="sum_cfu_disabled" msgid="5010617134210809853">"Ei käytössä"</string>
+ <string name="sum_cfu_disabled" msgid="5010617134210809853">"Ei päällä"</string>
<string name="labelCFB" msgid="615265213360512768">"Kun olen varattu"</string>
<string name="messageCFB" msgid="1958017270393563388">"Soita, kun numero on varattu:"</string>
<string name="sum_cfb_enabled" msgid="332037613072049492">"Puhelu siirretään numeroon <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
- <string name="sum_cfb_disabled" msgid="3589913334164866035">"Ei käytössä"</string>
+ <string name="sum_cfb_disabled" msgid="3589913334164866035">"Ei päällä"</string>
<string name="disable_cfb_forbidden" msgid="4831494744351633961">"Operaattorisi ei tue soitonsiirtojen poistamista käytöstä, kun puhelimesi on varattuna."</string>
<string name="labelCFNRy" msgid="3403533792248457946">"Kun en vastaa"</string>
<string name="messageCFNRy" msgid="7644434155765359009">"Soita, kun numero ei vastaa:"</string>
<string name="sum_cfnry_enabled" msgid="3000500837493854799">"Puhelu siirretään numeroon <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
- <string name="sum_cfnry_disabled" msgid="1990563512406017880">"Ei käytössä"</string>
+ <string name="sum_cfnry_disabled" msgid="1990563512406017880">"Ei päällä"</string>
<string name="disable_cfnry_forbidden" msgid="3174731413216550689">"Operaattorisi ei tue soitonsiirtojen poistamista käytöstä, kun puhelimesi ei vastaa."</string>
<string name="labelCFNRc" msgid="4163399350778066013">"Kun en ole tavoitettavissa"</string>
<string name="messageCFNRc" msgid="6980340731313007250">"Soita, kun numeroon ei saada yhteyttä:"</string>
<string name="sum_cfnrc_enabled" msgid="1799069234006073477">"Puhelu siirretään numeroon <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
- <string name="sum_cfnrc_disabled" msgid="739289696796917683">"Ei käytössä"</string>
+ <string name="sum_cfnrc_disabled" msgid="739289696796917683">"Ei päällä"</string>
<string name="disable_cfnrc_forbidden" msgid="775348748084726890">"Operaattorisi ei tue soitonsiirtojen poistamista käytöstä puhelimesi ollessa saavuttamattomissa."</string>
<string name="updating_title" msgid="6130548922615719689">"Puheluasetukset"</string>
<string name="call_settings_admin_user_only" msgid="7238947387649986286">"Vain järjestelmänvalvoja voi muuttaa puheluasetuksia."</string>
@@ -294,7 +294,7 @@
<string name="carrier_settings_euicc_summary" msgid="2027941166597330117">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> – <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>"</string>
<string name="mobile_data_settings_title" msgid="7228249980933944101">"Mobiilidata"</string>
<string name="mobile_data_settings_summary" msgid="5012570152029118471">"Käytä dataa mobiiliverkon kautta."</string>
- <string name="data_usage_disable_mobile" msgid="5669109209055988308">"Poistetaanko mobiilidata käytöstä?"</string>
+ <string name="data_usage_disable_mobile" msgid="5669109209055988308">"Laitetaanko mobiilidata pois päältä?"</string>
<string name="sim_selection_required_pref" msgid="6985901872978341314">"Valinta on pakollinen"</string>
<string name="sim_change_data_title" msgid="9142726786345906606">"Vaihdetaanko tied.siirto-SIM?"</string>
<string name="sim_change_data_message" msgid="3567358694255933280">"Käytetäänkö SIM-kortin <xliff:g id="NEW_SIM">%1$s</xliff:g> mobiilidataa kortin <xliff:g id="OLD_SIM">%2$s</xliff:g> sijaan?"</string>
@@ -545,7 +545,7 @@
<string name="incall_error_supp_service_hold" msgid="8535056414643540997">"Puhelujen pito ei onnistu."</string>
<string name="incall_error_wfc_only_no_wireless_network" msgid="5860742792811400109">"Yhdistä langattomaan verkkoon, jos haluat soittaa puhelun."</string>
<string name="incall_error_promote_wfc" msgid="9164896813931363415">"Ota Wi-Fi-puhelut käyttöön soittaaksesi."</string>
- <string name="emergency_information_hint" msgid="9208897544917793012">"Hätätilannetiedot"</string>
+ <string name="emergency_information_hint" msgid="9208897544917793012">"Vaaratiedot"</string>
<string name="emergency_information_owner_hint" msgid="6256909888049185316">"Omistaja"</string>
<string name="emergency_information_confirm_hint" msgid="5109017615894918914">"Katso tiedot napauttamalla uudelleen"</string>
<string name="emergency_enable_radio_dialog_title" msgid="2667568200755388829">"Hätäpuhelu"</string>
@@ -863,7 +863,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Oletusarvoisen data-SIM-kortin alitunnus:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL-kaistanleveys (kt/s):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL-kaistanleveys (kt/s):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Matkapuhelimen sijaintitiedot (poistettu käytöstä):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Fyysisen LTE-kanavan kokoonpano:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Matkapuhelintietojen päivitysaikaväli:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Matkapuhelimen kaikki mittaustiedot:"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index dbaa128..4c449d3 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Sous-identifiant de la carte SIM par défaut pour les données :"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Bande passante de téléchargement (kbit/s) :"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Bande passante d\'importation (kbit/s) :"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Informations de géolocalisation mobiles (obsolètes) :"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Configuration de la chaîne physique LTE :"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Fréquence d\'actualisation des informations mobiles :"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Toutes les informations mobiles liées aux mesures :"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 58b1d27..f8fa6a9 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Código de identificación secundario da SIM de datos predeterminada:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Largura de banda de descarga (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Largura de banda de carga (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Información da localización para móbiles (obsoleto):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Configuración de canle física de LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Taxa de actualización da información para móbiles:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Toda a información de medición para móbiles:"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 9d706a7..fd8b4f8 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"ડિફૉલ્ટ ડેટા સિમનું SubId:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL બૅન્ડવિડ્થ (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL બૅન્ડવિડ્થ (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"સેલ સ્થાન માહિતી (ટાળેલ):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE ભૌતિક ચૅનલની ગોઠવણી:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"સેલ માહિતી રિફ્રેશ થવાનો રેટ:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"તમામ સેલ માપ માહિતી:"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 2cdc768..de72f17 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"डिफ़ॉल्ट डेटा सिम का सब-आईडी:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"डीएल बैंडविड्थ (केबीपीएस):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"यूएल बैंडविड्थ (केबीपीएस):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"सेल की जगह की जानकारी (रोकी गयी है):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"एलटीई की फ़िज़िकल चैनल कॉन्फ़िगरेशन:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"सेल की जानकारी रीफ़्रेश होने की दर:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"सभी सेल के माप की पूरी जानकारी:"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 67cc6de..2951139 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -95,17 +95,17 @@
<string name="sum_loading_settings" msgid="434063780286688775">"Učitavanje postavki…"</string>
<string name="sum_hide_caller_id" msgid="131100328602371933">"Broj je skriven u izlaznim pozivima"</string>
<string name="sum_show_caller_id" msgid="3571854755324664591">"Broj prikazan za izlazne pozive"</string>
- <string name="sum_default_caller_id" msgid="1767070797135682959">"Koristiti zadane postavke operatera za prikaz mog broja kod odlaznih poziva"</string>
+ <string name="sum_default_caller_id" msgid="1767070797135682959">"Upotrijebi zadane postavke operatera za prikaz mog broja kod odlaznih poziva"</string>
<string name="labelCW" msgid="8449327023861428622">"Poziv na čekanju"</string>
- <string name="sum_cw_enabled" msgid="3977308526187139996">"Obavijesti me tijekom poziva o dolaznim pozivima"</string>
- <string name="sum_cw_disabled" msgid="3658094589461768637">"Obavijesti me tijekom poziva o dolaznim pozivima"</string>
+ <string name="sum_cw_enabled" msgid="3977308526187139996">"Obavijesti me o dolaznim pozivima tijekom poziva"</string>
+ <string name="sum_cw_disabled" msgid="3658094589461768637">"Obavijesti me o dolaznim pozivima tijekom poziva"</string>
<string name="call_forwarding_settings" msgid="8937130467468257671">"Postavke preusmjeravanja poziva"</string>
<string name="call_forwarding_settings_with_label" msgid="2345432813399564272">"Prosljeđivanje poziva (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="labelCF" msgid="3578719437928476078">"Preusmjeravanje poziva"</string>
<string name="labelCFU" msgid="8870170873036279706">"Uvijek preusmjeri"</string>
<string name="messageCFU" msgid="1361806450979589744">"Uvijek koristi ovaj broj"</string>
<string name="sum_cfu_enabled_indicator" msgid="9030139213402432776">"Preusmjeravanje svih poziva"</string>
- <string name="sum_cfu_enabled" msgid="5806923046528144526">"Preusmjeravanje poziva na <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
+ <string name="sum_cfu_enabled" msgid="5806923046528144526">"Preusmjeravanje svih poziva na <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
<string name="sum_cfu_enabled_no_number" msgid="7287752761743377930">"Broj je nedostupan"</string>
<string name="sum_cfu_disabled" msgid="5010617134210809853">"Isključeno"</string>
<string name="labelCFB" msgid="615265213360512768">"Kad je broj zauzet"</string>
@@ -863,7 +863,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SUBID zadanog SIM-a za podatkovni promet:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL propusnost (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL propusnost (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Informacije o lokaciji ćelije (obustavljeno):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Konfiguracija LTE fizičkog kanala:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Stopa osvježavanja informacija u ćeliji:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Sve informacije ćelija o mjerenju:"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index ff5d11b..616193f 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Alapértelmezett adatok SIM-alazonosítója:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Letöltési sávszélesség (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Feltöltési sávszélesség (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Mobilos helyadatok (megszűnt):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE fizikai csatorna konfigurációja:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Cellainformáció frissítési gyakorisága:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Minden cellamérési információ:"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index bc9945e..4e61083 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -102,7 +102,7 @@
<string name="call_forwarding_settings" msgid="8937130467468257671">"Վերահասցեավորում"</string>
<string name="call_forwarding_settings_with_label" msgid="2345432813399564272">"Զանգի փոխանցման կարգավորումներ (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="labelCF" msgid="3578719437928476078">"Զանգի վերահասցեավորում"</string>
- <string name="labelCFU" msgid="8870170873036279706">"Միշտ փոխանցել"</string>
+ <string name="labelCFU" msgid="8870170873036279706">"Միշտ վերահասցեավորել"</string>
<string name="messageCFU" msgid="1361806450979589744">"Միշտ օգտագործել այս համարը"</string>
<string name="sum_cfu_enabled_indicator" msgid="9030139213402432776">"Բոլոր զանգերը վերահասցեավորվում են"</string>
<string name="sum_cfu_enabled" msgid="5806923046528144526">"Բոլոր զանգերը վերահասցեավորվում են <xliff:g id="PHONENUMBER">{0}</xliff:g>-ին"</string>
@@ -113,12 +113,12 @@
<string name="sum_cfb_enabled" msgid="332037613072049492">"Վերահասցեավորվում է դեպի <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
<string name="sum_cfb_disabled" msgid="3589913334164866035">"Անջատված է"</string>
<string name="disable_cfb_forbidden" msgid="4831494744351633961">"Ձեր օպերատորը չի աջակցում զանգի վերահասցեավորման կասեցում, երբ ձեր հեռախոսը զբաղված է:"</string>
- <string name="labelCFNRy" msgid="3403533792248457946">"Երբ պատասխան չկա"</string>
+ <string name="labelCFNRy" msgid="3403533792248457946">"Երբ չեմ պատասխանում"</string>
<string name="messageCFNRy" msgid="7644434155765359009">"Թվել, երբ անպատասխան է"</string>
<string name="sum_cfnry_enabled" msgid="3000500837493854799">"Վերահասցեավորվում է դեպի <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
<string name="sum_cfnry_disabled" msgid="1990563512406017880">"Անջատված է"</string>
<string name="disable_cfnry_forbidden" msgid="3174731413216550689">"Ձեր օպերատորը չի աջակցում զանգի վերահասցեավորման կասեցում, երբ ձեր հեռախոսը չի պատասխանում:"</string>
- <string name="labelCFNRc" msgid="4163399350778066013">"Երբ անհասանելի է"</string>
+ <string name="labelCFNRc" msgid="4163399350778066013">"Երբ անհասանելի եմ"</string>
<string name="messageCFNRc" msgid="6980340731313007250">"Թվել, երբ անհասանելի է"</string>
<string name="sum_cfnrc_enabled" msgid="1799069234006073477">"Վերահասցեավորվում է դեպի <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
<string name="sum_cfnrc_disabled" msgid="739289696796917683">"Անջատված է"</string>
@@ -128,7 +128,7 @@
<string name="call_settings_with_label" msgid="8460230435361579511">"Կարգավորումներ (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="error_updating_title" msgid="2024290892676808965">"Զանգի կարգավորումների սխալ"</string>
<string name="reading_settings" msgid="1605904432450871183">"Ընթերցման կարգավորումներ..."</string>
- <string name="updating_settings" msgid="3650396734816028808">"Թարմացվում են կարգավորումները..."</string>
+ <string name="updating_settings" msgid="3650396734816028808">"Կարգավորումների թարմացում..."</string>
<string name="reverting_settings" msgid="7378668837291012205">"Կարգավորւմները հետադարձվում են..."</string>
<string name="response_error" msgid="3904481964024543330">"Անակնկալ պատասխան ցանցից:"</string>
<string name="exception_error" msgid="330994460090467">"Ցանցի կամ SIM քարտի սխալ"</string>
@@ -141,7 +141,7 @@
<string name="close_dialog" msgid="1074977476136119408">"Լավ"</string>
<string name="enable" msgid="2636552299455477603">"Միացնել"</string>
<string name="disable" msgid="1122698860799462116">"Անջատել"</string>
- <string name="change_num" msgid="6982164494063109334">"Նորացնել"</string>
+ <string name="change_num" msgid="6982164494063109334">"Թարմացնել"</string>
<string-array name="clir_display_values">
<item msgid="8477364191403806960">"Կանխադրված ցանց"</item>
<item msgid="6813323051965618926">"Թաքցնել համարը"</item>
@@ -863,7 +863,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SIM քարտի հավելյալ ID կանխադրված բջջային ինտերնետի համար՝"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL թողունակությունը (կբ/վ)՝"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL թողունակությունը (կբ/վ)՝"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Բջիջի տեղադրության տվյալներ (հնացած)՝"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE ֆիզիկական ալիքի կարգավորում՝"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Բջիջի տվյալների թարմացման հաճախականությունը՝"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Բոլոր բջիջների չափման տվյալները՝"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index e27ba14..9c4b99c 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -135,7 +135,7 @@
<string name="stk_cc_ss_to_dial_error" msgid="5147693491690618704">"Permintaan SS diubah ke panggilan reguler"</string>
<string name="stk_cc_ss_to_ussd_error" msgid="8330749347425752192">"Permintaan SS diubah ke permintaan USSD"</string>
<string name="stk_cc_ss_to_ss_error" msgid="8297155544652134278">"Diubah ke permintaan SS baru"</string>
- <string name="stk_cc_ss_to_dial_video_error" msgid="4255261231466032505">"Permintaan SS diubah ke video call"</string>
+ <string name="stk_cc_ss_to_dial_video_error" msgid="4255261231466032505">"Permintaan SS diubah ke panggilan video"</string>
<string name="fdn_check_failure" msgid="1833769746374185247">"Setelan Nomor Panggilan Tetap aplikasi Ponsel Anda diaktifkan. Akibatnya, beberapa fitur yang terkait dengan panggilan tidak bekerja."</string>
<string name="radio_off_error" msgid="8321564164914232181">"Keluar dari mode pesawat sebelum melihat setelan ini."</string>
<string name="close_dialog" msgid="1074977476136119408">"Oke"</string>
@@ -299,7 +299,7 @@
<string name="sim_change_data_title" msgid="9142726786345906606">"Ubah SIM data?"</string>
<string name="sim_change_data_message" msgid="3567358694255933280">"Gunakan <xliff:g id="NEW_SIM">%1$s</xliff:g> dan bukan <xliff:g id="OLD_SIM">%2$s</xliff:g> untuk data seluler?"</string>
<string name="wifi_calling_settings_title" msgid="5800018845662016507">"Panggilan Wi-Fi"</string>
- <string name="video_calling_settings_title" msgid="342829454913266078">"Video call operator"</string>
+ <string name="video_calling_settings_title" msgid="342829454913266078">"Panggilan video operator"</string>
<string name="gsm_umts_options" msgid="4968446771519376808">"Opsi GSM//UMTS"</string>
<string name="cdma_options" msgid="3669592472226145665">"Opsi CDMA"</string>
<string name="throttle_data_usage" msgid="1944145350660420711">"Penggunaan kuota"</string>
@@ -446,7 +446,7 @@
<string name="tty_mode_option_summary" msgid="4770510287236494371">"Setel Mode TTY"</string>
<string name="auto_retry_mode_title" msgid="2985801935424422340">"Coba lagi otomatis"</string>
<string name="auto_retry_mode_summary" msgid="2863919925349511402">"Aktifkan mode Coba lagi otomatis"</string>
- <string name="tty_mode_not_allowed_video_call" msgid="6551976083652752815">"Perubahan Mode TTY tidak diizinkan selama video call"</string>
+ <string name="tty_mode_not_allowed_video_call" msgid="6551976083652752815">"Perubahan Mode TTY tidak diizinkan selama panggilan video"</string>
<string name="menu_add" msgid="5616487894975773141">"Tambahkan kontak"</string>
<string name="menu_edit" msgid="3593856941552460706">"Edit kontak"</string>
<string name="menu_delete" msgid="6326861853830546488">"Hapus kontak"</string>
@@ -572,7 +572,7 @@
<string name="onscreenManageCallsText" msgid="1162047856081836469">"Kelola panggilan"</string>
<string name="onscreenManageConferenceText" msgid="4700574060601755137">"Kelola konferensi"</string>
<string name="onscreenAudioText" msgid="7224226735052019986">"Audio"</string>
- <string name="onscreenVideoCallText" msgid="1743992456126258698">"Video call"</string>
+ <string name="onscreenVideoCallText" msgid="1743992456126258698">"Panggilan video"</string>
<string name="importSimEntry" msgid="3892354284082689894">"Impor"</string>
<string name="importAllSimEntries" msgid="2628391505643564007">"Impor semua"</string>
<string name="importingSimContacts" msgid="4995457122107888932">"Mengimpor kontak dari SIM"</string>
@@ -583,7 +583,7 @@
<string name="hac_mode_summary" msgid="7774989500136009881">"Aktifkan kompatibilitas alat bantu dengar"</string>
<string name="rtt_mode_title" msgid="3075948111362818043">"Panggilan pesan real-time (RTT)"</string>
<string name="rtt_mode_summary" msgid="8631541375609989562">"Izinkan pengiriman pesan pada saat panggilan suara berlangsung"</string>
- <string name="rtt_mode_more_information" msgid="587500128658756318">"RTT membantu penelepon yang tunarungu, kesulitan mendengar, memiliki gangguan berbicara, atau memerlukan lebih dari sekadar suara.<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>Pelajari lebih lanjut</a>\n <br><br> - Panggilan RTT disimpan sebagai transkrip pesan\n <br> - RTT tidak tersedia untuk video call"</string>
+ <string name="rtt_mode_more_information" msgid="587500128658756318">"RTT membantu penelepon yang tunarungu, kesulitan mendengar, memiliki gangguan berbicara, atau memerlukan lebih dari sekadar suara.<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>Pelajari lebih lanjut</a>\n <br><br> - Panggilan RTT disimpan sebagai transkrip pesan\n <br> - RTT tidak tersedia untuk panggilan video"</string>
<string name="no_rtt_when_roaming" msgid="5268008247378355389">"Catatan: RTT tidak tersedia saat melakukan roaming"</string>
<string-array name="tty_mode_entries">
<item msgid="3238070884803849303">"TTY Nonaktif"</item>
@@ -654,8 +654,8 @@
<string name="voicemail_change_pin_dialog_title" msgid="4633077715231764435">"Ubah PIN"</string>
<string name="preference_category_ringtone" msgid="8787281191375434976">"Nada dering & Getar"</string>
<string name="pstn_connection_service_label" msgid="9200102709997537069">"Kartu SIM internal"</string>
- <string name="enable_video_calling_title" msgid="7246600931634161830">"Aktifkan video call"</string>
- <string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"Untuk mengaktifkan video call, Anda perlu mengaktifkan Mode LTE 4G yang Ditingkatkan di setelan jaringan."</string>
+ <string name="enable_video_calling_title" msgid="7246600931634161830">"Aktifkan panggilan video"</string>
+ <string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"Untuk mengaktifkan panggilan video, Anda perlu mengaktifkan Mode LTE 4G yang Ditingkatkan di setelan jaringan."</string>
<string name="enable_video_calling_dialog_settings" msgid="8697890611305307110">"Setelan jaringan"</string>
<string name="enable_video_calling_dialog_close" msgid="4298929725917045270">"Tutup"</string>
<string name="sim_label_emergency_calls" msgid="9078241989421522310">"Panggilan darurat"</string>
@@ -670,8 +670,8 @@
<string name="callFailed_cdma_call_limit" msgid="1074219746093031412">"Ada terlalu banyak panggilan aktif. Akhiri atau gabungkan panggilan yang ada sebelum melakukan panggilan baru."</string>
<string name="callFailed_imei_not_accepted" msgid="7257903653685147251">"Tidak dapat tersambung, masukkan kartu SIM yang valid."</string>
<string name="callFailed_wifi_lost" msgid="1788036730589163141">"Sambungan Wi-Fi terputus. Panggilan diakhiri."</string>
- <string name="dialFailed_low_battery" msgid="6857904237423407056">"Video call tidak bisa dilakukan karena daya baterai lemah."</string>
- <string name="callFailed_low_battery" msgid="4056828320214416182">"Video call berakhir karena daya baterai lemah."</string>
+ <string name="dialFailed_low_battery" msgid="6857904237423407056">"Panggilan video tidak bisa dilakukan karena daya baterai lemah."</string>
+ <string name="callFailed_low_battery" msgid="4056828320214416182">"Panggilan video berakhir karena daya baterai lemah."</string>
<string name="callFailed_emergency_call_over_wfc_not_available" msgid="5944309590693432042">"Panggilan darurat melalui panggilan Wi-Fi tidak tersedia di lokasi ini."</string>
<string name="callFailed_wfc_service_not_available_in_this_location" msgid="3624536608369524988">"Panggilan Wi-Fi tidak tersedia di lokasi ini."</string>
<string name="change_pin_title" msgid="3564254326626797321">"Ubah PIN Pesan Suara"</string>
@@ -817,7 +817,7 @@
<string name="radio_info_data_connection_enable" msgid="6183729739783252840">"Aktifkan Koneksi Data"</string>
<string name="radio_info_data_connection_disable" msgid="6404751291511368706">"Nonaktifkan Koneksi Data"</string>
<string name="volte_provisioned_switch_string" msgid="4812874990480336178">"VoLTE Disediakan"</string>
- <string name="vt_provisioned_switch_string" msgid="8295542122512195979">"Video Call Disediakan"</string>
+ <string name="vt_provisioned_switch_string" msgid="8295542122512195979">"Panggilan Video Disediakan"</string>
<string name="wfc_provisioned_switch_string" msgid="3835004640321078988">"Panggilan Wi-Fi Disediakan"</string>
<string name="eab_provisioned_switch_string" msgid="4449676720736033035">"EAB/Presence Disediakan"</string>
<string name="cbrs_data_switch_string" msgid="6060356430838077653">"Data Cbrs"</string>
@@ -836,7 +836,7 @@
<string name="radio_info_ims_reg_status_not_registered" msgid="8045821447288876085">"Tidak Terdaftar"</string>
<string name="radio_info_ims_feature_status_available" msgid="6493200914756969292">"Tersedia"</string>
<string name="radio_info_ims_feature_status_unavailable" msgid="8930391136839759778">"Tidak tersedia"</string>
- <string name="radio_info_ims_reg_status" msgid="25582845222446390">"Pendaftaran IMS: <xliff:g id="STATUS">%1$s</xliff:g>\nSuara melalui LTE: <xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nSuara melalui Wi-Fi: <xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\nVideo Call: <xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nAntarmuka UT: <xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
+ <string name="radio_info_ims_reg_status" msgid="25582845222446390">"Pendaftaran IMS: <xliff:g id="STATUS">%1$s</xliff:g>\nSuara melalui LTE: <xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nSuara melalui Wi-Fi: <xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\nPanggilan Video: <xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nAntarmuka UT: <xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
<string name="radioInfo_service_in" msgid="45753418231446400">"Dalam Layanan"</string>
<string name="radioInfo_service_out" msgid="287972405416142312">"Di Luar Area Layanan"</string>
<string name="radioInfo_service_emergency" msgid="4763879891415016848">"Panggilan Darurat Saja"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SubId SIM data default:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Bandwidth DL (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Bandwidth UL (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Info Lokasi Sel (tidak digunakan lagi):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Konfigurasi Saluran Fisik LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Rasio Pembaruan Info Sel"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Semua Info Pengukuran Sel:"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index e6f6150..0c7c7a1 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -95,7 +95,7 @@
<string name="sum_loading_settings" msgid="434063780286688775">"Caricamento impostazioni…"</string>
<string name="sum_hide_caller_id" msgid="131100328602371933">"Numero nascosto per chiamate in uscita"</string>
<string name="sum_show_caller_id" msgid="3571854755324664591">"Numero visualizzato in chiamate in uscita"</string>
- <string name="sum_default_caller_id" msgid="1767070797135682959">"Usa impostazioni dell\'operatore per mostrare il mio numero nelle chiamate in uscita"</string>
+ <string name="sum_default_caller_id" msgid="1767070797135682959">"Usa le impostazioni predefinite dell\'operatore per mostrare il mio numero nelle chiamate in uscita"</string>
<string name="labelCW" msgid="8449327023861428622">"Avviso di chiamata"</string>
<string name="sum_cw_enabled" msgid="3977308526187139996">"Notifica chiamate in entrata durante telefonata"</string>
<string name="sum_cw_disabled" msgid="3658094589461768637">"Notifica chiamate in entrata durante telefonata"</string>
@@ -863,7 +863,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"ID secondario della SIM dati predefinita:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Larghezza di banda DL (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Larghezza di banda UL (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Informazioni sulla posizione delle celle (obsoleto):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Configurazione canale fisico LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Frequenza di aggiornamento delle informazioni sulle celle:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Informazioni sulla misurazione di tutte le celle:"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 4780834..c2121c3 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -865,7 +865,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"תת-מזהה של כרטיס ה-SIM עם חבילת גלישה המוגדר כברירת מחדל:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"רוחב פס DL (ב-kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"רוחב פס UL (ב-kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"מידע על מיקום סלולרי (הוצא משימוש):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"תצורת ערוץ פיזי של LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"קצב רענון של מידע סלולרי:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"כל המידע של מדידה סלולרית:"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 3177d31..be4fd0d 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"デフォルトのデータ SIM の SUBID:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL 帯域幅(kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL 帯域幅(kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"セルの位置情報(サポート終了):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE の物理チャネル設定:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"セル情報の更新間隔:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"すべてのセルの測定情報:"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 3c3249b..bcfa63d 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -74,7 +74,7 @@
<string name="phone_accounts_configure_account_settings" msgid="6622119715253196586">"ანგარიშის პარამეტრების კონფიგურაცია"</string>
<string name="phone_accounts_all_calling_accounts" msgid="1609600743500618823">"დარეკვის ყველა ანგარიში"</string>
<string name="phone_accounts_all_calling_accounts_summary" msgid="2214134955430107240">"აირჩიეთ რომელ ანგარიშს შეეძლება ზარების განხორციელება"</string>
- <string name="wifi_calling" msgid="3650509202851355742">"დარეკვა Wi-Fi-ს მეშვეობით"</string>
+ <string name="wifi_calling" msgid="3650509202851355742">"Wi-Fi დარეკვა"</string>
<string name="connection_service_default_label" msgid="7332739049855715584">"კავშირის ჩაშენებული სერვისი"</string>
<string name="voicemail" msgid="7697769412804195032">"ხმოვანი ფოსტა"</string>
<string name="voicemail_settings_with_label" msgid="4228431668214894138">"ხმოვანი ფოსტა (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"მონაცემების ნაგულისხმევი SIM-ის subId:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL-არხის გამტარუნარიანობა (კბიტ/წმ):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL-არხის გამტარუნარიანობა (კბიტ/წმ):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"ფიჭური მდებარეობის ინფორმაცია (მოძველდა):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE ფიზიკური არხის კონფიგურაცია:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"ფიჭური ინფორმაციის განახლების სიხშირე:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"სრული ინფორმაცია ფიჭური ქსელის შესახებ:"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 610a133..e3c38fa 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -24,7 +24,7 @@
<string name="unknown" msgid="8279698889921830815">"មិនស្គាល់"</string>
<string name="private_num" msgid="4487990167889159992">"លេខឯកជន"</string>
<string name="payphone" msgid="7936735771836716941">"ទូរស័ព្ទសាធារណៈ"</string>
- <string name="onHold" msgid="6132725550015899006">"រង់ចាំ"</string>
+ <string name="onHold" msgid="6132725550015899006">"កំពុងរង់ចាំ"</string>
<string name="carrier_mmi_msg_title" msgid="6050165242447507034">"សារ <xliff:g id="MMICARRIER">%s</xliff:g>"</string>
<string name="default_carrier_mmi_msg_title" msgid="7754317179938537213">"សារក្រុមហ៊ុនសេវាទូរសព្ទ"</string>
<string name="mmiStarted" msgid="9212975136944568623">"បានចាប់ផ្ដើមកូដ MMI"</string>
@@ -91,7 +91,7 @@
<string name="additional_cdma_call_settings" msgid="2178016561980611304">"កំណត់ការហៅ CDMA បន្ថែម"</string>
<string name="sum_cdma_call_settings" msgid="3185825305136993636">"កំណត់ការហៅតែ CDMA បន្ថែមប៉ុណ្ណោះ"</string>
<string name="labelNwService" msgid="6015891883487125120">"ការកំណត់សេវាបណ្ដាញ"</string>
- <string name="labelCallerId" msgid="2090540744550903172">"លេខសម្គាល់អ្នកហៅ"</string>
+ <string name="labelCallerId" msgid="2090540744550903172">"អត្តសញ្ញាណអ្នកហៅទូរសព្ទ"</string>
<string name="sum_loading_settings" msgid="434063780286688775">"កំពុងផ្ទុកការកំណត់…"</string>
<string name="sum_hide_caller_id" msgid="131100328602371933">"បានលាក់លេខក្នុងការហៅចេញ"</string>
<string name="sum_show_caller_id" msgid="3571854755324664591">"បានបង្ហាញលេខក្នុងការហៅចេញ"</string>
@@ -99,8 +99,8 @@
<string name="labelCW" msgid="8449327023861428622">"រង់ចាំការហៅ"</string>
<string name="sum_cw_enabled" msgid="3977308526187139996">"ក្នុងអំឡុងពេលហៅ ជូនដំណឹងដល់ខ្ញុំអំពីការហៅចូល"</string>
<string name="sum_cw_disabled" msgid="3658094589461768637">"ក្នុងអំឡុងពេលហៅ ជូនដំណឹងដល់ខ្ញុំអំពីការហៅចូល"</string>
- <string name="call_forwarding_settings" msgid="8937130467468257671">"កំណត់ការហៅបញ្ជូនបន្ត"</string>
- <string name="call_forwarding_settings_with_label" msgid="2345432813399564272">"ការកំណត់ការបញ្ជូនការហៅបន្ត (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
+ <string name="call_forwarding_settings" msgid="8937130467468257671">"ការកំណត់ការបញ្ជូនបន្តការហៅទូរសព្ទ"</string>
+ <string name="call_forwarding_settings_with_label" msgid="2345432813399564272">"ការកំណត់ការបញ្ជូនបន្តការហៅទូរសព្ទ (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="labelCF" msgid="3578719437928476078">"បញ្ជូនការហៅបន្ត"</string>
<string name="labelCFU" msgid="8870170873036279706">"បញ្ជូនបន្តជានិច្ច"</string>
<string name="messageCFU" msgid="1361806450979589744">"ប្រើលេខនេះជានិច្ច"</string>
@@ -128,7 +128,7 @@
<string name="call_settings_with_label" msgid="8460230435361579511">"ការកំណត់ (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="error_updating_title" msgid="2024290892676808965">"បញ្ហាការកំណត់ការហៅ"</string>
<string name="reading_settings" msgid="1605904432450871183">"កំពុងអានការកំណត់…"</string>
- <string name="updating_settings" msgid="3650396734816028808">"កំណត់ការធ្វើបច្ចុប្បន្នភាព…"</string>
+ <string name="updating_settings" msgid="3650396734816028808">"កំពុងធ្វើបច្ចុប្បន្នភាពការកំណត់…"</string>
<string name="reverting_settings" msgid="7378668837291012205">"ត្រឡប់ការកំណត់…"</string>
<string name="response_error" msgid="3904481964024543330">"ការឆ្លើយតបដែលមិនរំពឹងទុកពីបណ្ដាញ។"</string>
<string name="exception_error" msgid="330994460090467">"បញ្ហាស៊ីមកាត ឬបណ្ដាញ។"</string>
@@ -298,7 +298,7 @@
<string name="sim_selection_required_pref" msgid="6985901872978341314">"ជម្រើសដែលត្រូវមាន"</string>
<string name="sim_change_data_title" msgid="9142726786345906606">"ប្ដូរស៊ីមទិន្នន័យ?"</string>
<string name="sim_change_data_message" msgid="3567358694255933280">"ប្រើប្រាស់ <xliff:g id="NEW_SIM">%1$s</xliff:g> ជំនួសឲ្យ <xliff:g id="OLD_SIM">%2$s</xliff:g> សម្រាប់ទិន្នន័យទូរសព្ទចល័តដែរឬទេ?"</string>
- <string name="wifi_calling_settings_title" msgid="5800018845662016507">"ការហៅទូរសព្ទតាម Wi-Fi"</string>
+ <string name="wifi_calling_settings_title" msgid="5800018845662016507">"ការហៅតាម Wi-Fi"</string>
<string name="video_calling_settings_title" msgid="342829454913266078">"ការហៅវីដេអូតាមក្រុមផ្ដល់សេវាទូរសព្ទ"</string>
<string name="gsm_umts_options" msgid="4968446771519376808">"ជម្រើស GSM/UMTS"</string>
<string name="cdma_options" msgid="3669592472226145665">"ជម្រើស CDMA"</string>
@@ -513,7 +513,7 @@
<string name="card_title_conf_call" msgid="901197309274457427">"ការហៅជាក្រុម"</string>
<string name="card_title_incoming_call" msgid="881424648458792430">"ការហៅចូល"</string>
<string name="card_title_call_ended" msgid="650223980095026340">"បានបញ្ចប់ការហៅ"</string>
- <string name="card_title_on_hold" msgid="9028319436626975207">"រង់ចាំ"</string>
+ <string name="card_title_on_hold" msgid="9028319436626975207">"កំពុងរង់ចាំ"</string>
<string name="card_title_hanging_up" msgid="814874106866647871">"បញ្ចប់ការសន្ទនា"</string>
<string name="card_title_in_call" msgid="8231896539567594265">"ការហៅចូល"</string>
<string name="notification_voicemail_title" msgid="3932876181831601351">"សារជាសំឡេងថ្មី"</string>
@@ -839,7 +839,7 @@
<string name="radio_info_ims_reg_status" msgid="25582845222446390">"ការចុះឈ្មោះ IMS៖ <xliff:g id="STATUS">%1$s</xliff:g>\nការហៅជាសំឡេងតាម LTE៖ <xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nការហៅជាសំឡេងតាម WiFi៖ <xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\nការហៅជាវីដេអូ៖ <xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nផ្ទៃ UT ៖ <xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
<string name="radioInfo_service_in" msgid="45753418231446400">"កំពុងដំណើរការ"</string>
<string name="radioInfo_service_out" msgid="287972405416142312">"មិនដំណើរការ"</string>
- <string name="radioInfo_service_emergency" msgid="4763879891415016848">"សម្រាប់តែការហៅទៅលេខសង្គ្រោះបន្ទាន់ប៉ុណ្ណោះ"</string>
+ <string name="radioInfo_service_emergency" msgid="4763879891415016848">"ការហៅទៅលេខសង្គ្រោះបន្ទាន់តែប៉ុណ្ណោះ"</string>
<string name="radioInfo_service_off" msgid="3456583511226783064">"វិទ្យុបានបិទ"</string>
<string name="radioInfo_roaming_in" msgid="3156335577793145965">"រ៉ូមីង"</string>
<string name="radioInfo_roaming_not" msgid="1904547918725478110">"មិនរ៉ូមីងទេ"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"លេខសម្គាល់រងរបស់ស៊ីមទិន្នន័យលំនាំដើម៖"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"កម្រិតបញ្ជូន DL (kbps) ៖"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"កម្រិតបញ្ជូន UL (kbps) ៖"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"ព័ត៌មានទីតាំងទូរសព្ទចល័ត (បានបញ្ឈប់)៖"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"ការកំណត់រចនាសម្ព័ន្ធបណ្ដាញរូបវ័ន្ត LTE ៖"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"អត្រាផ្ទុកឡើងវិញនៃព័ត៌មានទូរសព្ទចល័ត៖"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"ព័ត៌មានវាស់ទូរសព្ទចល័តទាំងអស់៖"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 5dc6cdd..3e4ea79 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -128,7 +128,7 @@
<string name="call_settings_with_label" msgid="8460230435361579511">"ಸೆಟ್ಟಿಂಗ್ಗಳು (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="error_updating_title" msgid="2024290892676808965">"ಕರೆ ಸೆಟ್ಟಿಂಗ್ಗಳ ದೋಷ"</string>
<string name="reading_settings" msgid="1605904432450871183">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಓದಲಾಗುತ್ತಿದೆ…"</string>
- <string name="updating_settings" msgid="3650396734816028808">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ನವೀಕರಿಸಲಾಗುತ್ತಿದೆ…"</string>
+ <string name="updating_settings" msgid="3650396734816028808">"ಸೆಟ್ಟಿಂಗ್ಗಳು ಅಪ್ಡೇಟ್ ಆಗುತ್ತಿವೆ…"</string>
<string name="reverting_settings" msgid="7378668837291012205">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಹಿಂತಿರುಗಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="response_error" msgid="3904481964024543330">"ನೆಟ್ವರ್ಕ್ನಿಂದ ಅನಿರೀಕ್ಷಿತ ಪ್ರತಿಕ್ರಿಯೆ."</string>
<string name="exception_error" msgid="330994460090467">"ನೆಟ್ವರ್ಕ್ ಅಥವಾ ಸಿಮ್ ಕಾರ್ಡ್ ದೋಷ."</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"ಡೀಫಾಲ್ಟ್ ಡೇಟಾ ಸಿಮ್ನ ವಿಷಯಐಡಿ:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL ಬ್ಯಾಂಡ್ವಿಡ್ತ್ (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL ಬ್ಯಾಂಡ್ವಿಡ್ತ್ (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"ಸೆಲ್ ಸ್ಥಳ ಮಾಹಿತಿ (ತಡೆಹಿಡಿಯಲಾಗಿದೆ):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE ಭೌತಿಕ ಚಾನೆಲ್ ಕಾನ್ಫಿಗರೇಶನ್:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"ಸೆಲ್ ಮಾಹಿತಿ ರಿಫ್ರೆಶ್ ದರ:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"ಎಲ್ಲಾ ಸೆಲ್ ಮಾಪನ ಮಾಹಿತಿ:"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 3729c99..914f39a 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -511,7 +511,7 @@
<string name="card_title_dialing" msgid="8742182654254431781">"전화 거는 중"</string>
<string name="card_title_redialing" msgid="18130232613559964">"재다이얼 중"</string>
<string name="card_title_conf_call" msgid="901197309274457427">"다자간 통화"</string>
- <string name="card_title_incoming_call" msgid="881424648458792430">"수신전화"</string>
+ <string name="card_title_incoming_call" msgid="881424648458792430">"수신 전화"</string>
<string name="card_title_call_ended" msgid="650223980095026340">"통화 종료됨"</string>
<string name="card_title_on_hold" msgid="9028319436626975207">"대기 중"</string>
<string name="card_title_hanging_up" msgid="814874106866647871">"전화 끊는 중"</string>
@@ -572,7 +572,7 @@
<string name="onscreenManageCallsText" msgid="1162047856081836469">"통화 관리"</string>
<string name="onscreenManageConferenceText" msgid="4700574060601755137">"다자간 통화 관리"</string>
<string name="onscreenAudioText" msgid="7224226735052019986">"오디오"</string>
- <string name="onscreenVideoCallText" msgid="1743992456126258698">"화상 통화"</string>
+ <string name="onscreenVideoCallText" msgid="1743992456126258698">"영상 통화"</string>
<string name="importSimEntry" msgid="3892354284082689894">"가져오기"</string>
<string name="importAllSimEntries" msgid="2628391505643564007">"모두 가져오기"</string>
<string name="importingSimContacts" msgid="4995457122107888932">"SIM 주소록 가져오는 중"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"기본 데이터 SIM의 subId:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL 대역폭(kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL 대역폭(kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"셀 위치 정보(사용 중단됨):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE 물리적 채널 구성:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"셀 정보 새로고침 빈도:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"모든 셀 측정 정보:"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index f0804b1..f2220fb 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -48,7 +48,7 @@
<string name="no_vm_number_msg" msgid="5165161462411372504">"SIM-картада сакталган үн почтасынын номери жок."</string>
<string name="add_vm_number_str" msgid="7368168964435881637">"Номер кошуу"</string>
<string name="voice_number_setting_primary_user_only" msgid="3394706575741912843">"Үн почта жөндөөлөрүн алгачкы колдонуучу гана өзгөртө алат."</string>
- <string name="puk_unlocked" msgid="4627340655215746511">"SIM-картаңыз бөгөттөн чыгарылган. Телефонуңуздун кулпусу ачылууда…"</string>
+ <string name="puk_unlocked" msgid="4627340655215746511">"SIM картаңыз бөгөттөн чыгарылган. Телефонуңуздун кулпусу ачылууда…"</string>
<string name="label_ndp" msgid="7617392683877410341">"SIM-карта тармагынын кулпусун ачуучу PIN код"</string>
<string name="sim_ndp_unlock_text" msgid="7737338355451978338">"Кулпусун ачуу"</string>
<string name="sim_ndp_dismiss_text" msgid="89667342248929777">"Этибарга албоо"</string>
@@ -170,7 +170,7 @@
<string name="empty_networks_list" msgid="9216418268008582342">"Эч тармак табылган жок."</string>
<string name="network_query_error" msgid="3862515805115145124">"Тармактар табылбай койду. Кайталап көрүңүз."</string>
<string name="register_on_network" msgid="4194770527833960423">"<xliff:g id="NETWORK">%s</xliff:g> тармагына катталууда…"</string>
- <string name="not_allowed" msgid="8541221928746104798">"Сиздин SIM-картаңыз бул түйүнгө кошулганга жол бербейт."</string>
+ <string name="not_allowed" msgid="8541221928746104798">"Сиздин SIM картаңыз бул түйүнгө кошулганга жол бербейт."</string>
<string name="connect_later" msgid="1950138106010005425">"Бул түйүнгө азыр кошулуу мүмкүн эмес. Бир аздан соң кайра аракеттениңиз."</string>
<string name="registration_done" msgid="5337407023566953292">"Тармакка катталды."</string>
<string name="already_auto" msgid="8607068290733079336">"Мурунтан эле автоматтык түрдө тандоодо."</string>
@@ -464,9 +464,9 @@
<string name="delete_fdn_contact" msgid="7027405651994507077">"Туруктуу терүү номерин жок кылуу"</string>
<string name="deleting_fdn_contact" msgid="6872320570844460428">"Туруктуу терүү номери жок кылынууда…"</string>
<string name="fdn_contact_deleted" msgid="1680714996763848838">"Туруктуу терүү номери өчүрүлдү."</string>
- <string name="pin2_invalid" msgid="2313954262684494442">"БНТ жаңыртылган жок, анткени туура эмес PIN-код киргизилди."</string>
- <string name="fdn_invalid_number" msgid="9067189814657840439">"Уруксат берилген номер жаңыртылган жок, себеби жазылган номердин саны <xliff:g id="FDN_NUMBER_LIMIT_LENGTH">%d</xliff:g> ашпашы керек."</string>
- <string name="pin2_or_fdn_invalid" msgid="7542639487955868181">"БНТ жаңыртылган жок. PIN2 туура эмес, же телефон номуру жараксыз."</string>
+ <string name="pin2_invalid" msgid="2313954262684494442">"БНТ жаңырган жок, анткени туура эмес PIN-код киргизилди."</string>
+ <string name="fdn_invalid_number" msgid="9067189814657840439">"Уруксат берилген номер жаңырган жок, себеби жазылган номердин саны <xliff:g id="FDN_NUMBER_LIMIT_LENGTH">%d</xliff:g> ашпашы керек."</string>
+ <string name="pin2_or_fdn_invalid" msgid="7542639487955868181">"БНТ жаңырган жок. PIN2 туура эмес, же телефон номуру жараксыз."</string>
<string name="fdn_failed" msgid="216592346853420250">"БНТ иши кыйрады."</string>
<string name="simContacts_emptyLoading" msgid="4989040293858675483">"SIM-картадан окулууда…"</string>
<string name="simContacts_empty" msgid="1135632055473689521">"SIM картаңызда байланыштар жок."</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Демейки оператордун SIM картасынын көз салуу идентификатору:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL өткөрүү жөндөмдүүлүгү (кб/сек.):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL өткөрүү жөндөмдүүлүгү (кб/сек.):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Уюлдук тармакта жайгашкан жери тууралуу маалымат (жоюлган):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE физикалык каналынын конфигурациясы:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Уюлдук маалыматты жаңылоо ылдамдыгы:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Уюлдук чен-өлчөм маалыматтары:"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index ddb18fb..ac648ad 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -127,7 +127,7 @@
<string name="call_settings_admin_user_only" msgid="7238947387649986286">"Skambučių nustatymus gali keisti tik administruojantis naudotojas."</string>
<string name="call_settings_with_label" msgid="8460230435361579511">"Nustatymai (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="error_updating_title" msgid="2024290892676808965">"Skambinimo nustatymų klaida"</string>
- <string name="reading_settings" msgid="1605904432450871183">"Skaitomi nustatymai..."</string>
+ <string name="reading_settings" msgid="1605904432450871183">"Analizuojami nustatymai..."</string>
<string name="updating_settings" msgid="3650396734816028808">"Atnaujinami nustatymai..."</string>
<string name="reverting_settings" msgid="7378668837291012205">"Grąžinami nustatymai…"</string>
<string name="response_error" msgid="3904481964024543330">"Netikėtas atsakas iš tinklo."</string>
@@ -865,7 +865,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Numatytųjų duomenų SIM kortelės papildomas ID:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL pralaidumas (Kb/s):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL pralaidumas (Kb/s):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Mobiliojo ryšio vietovės informacija (nebenaudojama):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE fizinio kanalo konfigūracija:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Mobiliojo ryšio informacijos atnaujinimo dažnis:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Visų mobiliųjų ryšių įvertinimo informacija:"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 1167e3d..b215de2 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -863,7 +863,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SUBID на стандардната SIM за мобилен интернет:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Брзина на пренос при преземање (кбит/с):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Брзина на пренос при прикачување (кбит/с):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Информации за локација на мобилен (неподдржано):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Конфигурација на физички канал на LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Стапка на освежување на информациите за мобилниот:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Сите информации за мерењата на мобилниот:"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 1d65c97..2e4e8c2 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -95,11 +95,11 @@
<string name="sum_loading_settings" msgid="434063780286688775">"ക്രമീകരണങ്ങൾ ലോഡുചെയ്യുന്നു…"</string>
<string name="sum_hide_caller_id" msgid="131100328602371933">"ഔട്ട്ഗോയിംഗ് കോളുകളിൽ നമ്പർ മറച്ചിരിക്കുന്നു"</string>
<string name="sum_show_caller_id" msgid="3571854755324664591">"ഔട്ട്ഗോയിംഗ് കോളുകളിൽ നമ്പർ ദൃശ്യമാക്കിയിരിക്കുന്നു"</string>
- <string name="sum_default_caller_id" msgid="1767070797135682959">"ഔട്ട്ഗോയിംഗ് കോളുകളിൽ എന്റെ നമ്പർ ദൃശ്യമാക്കാൻ സ്ഥിര ഓപ്പറേറ്റർ ക്രമീകരണങ്ങൾ ഉപയോഗിക്കുക"</string>
+ <string name="sum_default_caller_id" msgid="1767070797135682959">"ഔട്ട്ഗോയിംഗ് കോളുകളിൽ എന്റെ നമ്പർ കാണിക്കാൻ ഡിഫോൾട്ട് ഓപ്പറേറ്റർ ക്രമീകരണങ്ങൾ ഉപയോഗിക്കുക"</string>
<string name="labelCW" msgid="8449327023861428622">"കോൾ വെയ്റ്റിംഗ്"</string>
<string name="sum_cw_enabled" msgid="3977308526187139996">"ഒരു കോളിനിടയിൽ, ഇൻകമിംഗ് കോളുകളെക്കുറിച്ച് എന്നെ അറിയിക്കുക"</string>
<string name="sum_cw_disabled" msgid="3658094589461768637">"ഒരു കോളിനിടയിൽ, ഇൻകമിംഗ് കോളുകളെക്കുറിച്ച് എന്നെ അറിയിക്കുക"</string>
- <string name="call_forwarding_settings" msgid="8937130467468257671">"കോൾഫോർവേഡിംഗ് ക്രമീകരണം"</string>
+ <string name="call_forwarding_settings" msgid="8937130467468257671">"കോൾ ഫോർവേഡിംഗ് ക്രമീകരണം"</string>
<string name="call_forwarding_settings_with_label" msgid="2345432813399564272">"കോൾഫോർവേഡിംഗ് ക്രമീകരണം (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="labelCF" msgid="3578719437928476078">"കോൾ ഫോർവേഡിംഗ്"</string>
<string name="labelCFU" msgid="8870170873036279706">"എല്ലായ്പ്പോഴും കൈമാറുക"</string>
@@ -127,8 +127,8 @@
<string name="call_settings_admin_user_only" msgid="7238947387649986286">"അഡ്മിൻ ഉപയോക്താവിന് മാത്രമേ കോൾ ക്രമീകരണം മാറ്റാൻ കഴിയൂ."</string>
<string name="call_settings_with_label" msgid="8460230435361579511">"ക്രമീകരണം (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="error_updating_title" msgid="2024290892676808965">"കോൾ ക്രമീകരണ പിശക്"</string>
- <string name="reading_settings" msgid="1605904432450871183">"ക്രമീകരണങ്ങൾ റീഡുചെയ്യുന്നു.…"</string>
- <string name="updating_settings" msgid="3650396734816028808">"ക്രമീകരണങ്ങൾ അപ്ഡേറ്റുചെയ്യുന്നു…"</string>
+ <string name="reading_settings" msgid="1605904432450871183">"ക്രമീകരണങ്ങൾ റീഡ് ചെയ്യുന്നു.…"</string>
+ <string name="updating_settings" msgid="3650396734816028808">"ക്രമീകരണങ്ങൾ അപ്ഡേറ്റ് ചെയ്യുന്നു…"</string>
<string name="reverting_settings" msgid="7378668837291012205">"ക്രമീകരണങ്ങൾ പഴയപടിയാക്കുന്നു…"</string>
<string name="response_error" msgid="3904481964024543330">"നെറ്റ്വർക്കിൽ നിന്നുള്ള അപ്രതീക്ഷിത പ്രതികരണം."</string>
<string name="exception_error" msgid="330994460090467">"നെറ്റ്വർക്ക് അല്ലെങ്കിൽ സിം കാർഡ് പിശക്."</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"ഡിഫോൾട്ട് ഡാറ്റാ സിമ്മിന്റെ ഉപഐഡി:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL ബാൻഡ്വിത്ത് (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL ബാൻഡ്വിത്ത് (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"സെൽ ലൊക്കേഷൻ വിവരങ്ങൾ (അവസാനിപ്പിച്ചു):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE ഫിസിക്കൽ ചാനൽ കോൺഫിഗറേഷൻ:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"സെൽ വിവരങ്ങൾ പുതുക്കിയെടുക്കൽ നിരക്ക്:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"എല്ലാ സെൽ അളവ് വിവരങ്ങളും:"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 09e6b64..3cccaae 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -62,7 +62,7 @@
<string name="labelCdmaMore_with_label" msgid="7759692829160238152">"CDMA дуудлагын тохиргоо (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="apn_settings" msgid="1978652203074756623">"Хандалтын Цэгийн Нэрс"</string>
<string name="settings_label" msgid="9101778088412567956">"Сүлжээний тохиргоо"</string>
- <string name="phone_accounts" msgid="1216879437523774604">"Дуудах бүртгэл"</string>
+ <string name="phone_accounts" msgid="1216879437523774604">"Дуудлагын бүртгэлүүд"</string>
<string name="phone_accounts_make_calls_with" msgid="16747814788918145">"Дараахаар дуудлага хийх"</string>
<string name="phone_accounts_make_sip_calls_with" msgid="4691221006731847255">"Дараахаар SIP дуудлага хийх"</string>
<string name="phone_accounts_ask_every_time" msgid="6192347582666047168">"Эхлэж хандах"</string>
@@ -102,10 +102,10 @@
<string name="call_forwarding_settings" msgid="8937130467468257671">"Дуудлага шилжүүлэх тохиргоо"</string>
<string name="call_forwarding_settings_with_label" msgid="2345432813399564272">"Дуудлага шилжүүлэх тохиргоонууд (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="labelCF" msgid="3578719437928476078">"Дуудлага дамжуулах"</string>
- <string name="labelCFU" msgid="8870170873036279706">"Байнга дамжуулах"</string>
+ <string name="labelCFU" msgid="8870170873036279706">"Байнга шилжүүлэх"</string>
<string name="messageCFU" msgid="1361806450979589744">"Байнга энэ дугаарыг ашиглах"</string>
<string name="sum_cfu_enabled_indicator" msgid="9030139213402432776">"Бүх дуудлагыг дамжуулж байна"</string>
- <string name="sum_cfu_enabled" msgid="5806923046528144526">"Бүх дуудлагыг <xliff:g id="PHONENUMBER">{0}</xliff:g> руу дамжуулж байна"</string>
+ <string name="sum_cfu_enabled" msgid="5806923046528144526">"Бүх дуудлагыг <xliff:g id="PHONENUMBER">{0}</xliff:g> руу шилжүүлж байна"</string>
<string name="sum_cfu_enabled_no_number" msgid="7287752761743377930">"Дугаар холбогдох боломжгүй"</string>
<string name="sum_cfu_disabled" msgid="5010617134210809853">"Идэвхгүй"</string>
<string name="labelCFB" msgid="615265213360512768">"Завгүй бол"</string>
@@ -425,7 +425,7 @@
<string name="cdma_activate_device" msgid="5914720276140097632">"Төхөөрөмжийг идэвхжүүлэх"</string>
<string name="cdma_lte_data_service" msgid="359786441782404562">"Дата үйлчилгээний тохиргоо"</string>
<string name="carrier_settings_title" msgid="6292869148169850220">"Операторын тохиргоо"</string>
- <string name="fdn" msgid="2545904344666098749">"Тогтсон Залгах Дугаарууд"</string>
+ <string name="fdn" msgid="2545904344666098749">"Тогтсон залгах дугаарууд"</string>
<string name="fdn_with_label" msgid="6412087553365709494">"Тогтсон Дуудлагын Дугаарууд(<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="manage_fdn_list" msgid="3341716430375195441">"FDN жагсаалт"</string>
<string name="fdn_list_with_label" msgid="1409655283510382556">"FDN жагсаалт (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
@@ -562,7 +562,7 @@
<string name="fire_type_description" msgid="6565200468934914930">"Гал"</string>
<string name="description_concat_format" msgid="2014471565101724088">"%1$s, %2$s"</string>
<string name="dialerKeyboardHintText" msgid="1115266533703764049">"Залгахдаа гар ашиглах"</string>
- <string name="onscreenHoldText" msgid="4025348842151665191">"Саатуулах"</string>
+ <string name="onscreenHoldText" msgid="4025348842151665191">"Хүлээлгэх"</string>
<string name="onscreenEndCallText" msgid="6138725377654842757">"Дуусгах"</string>
<string name="onscreenShowDialpadText" msgid="658465753816164079">"Диалпад"</string>
<string name="onscreenMuteText" msgid="5470306116733843621">"Дууг хаах"</string>
@@ -625,8 +625,8 @@
<string name="phone_in_ecm_call_notification_text" msgid="653972232922670335">"Дата холболтыг идэвхгүйжүүлсэн"</string>
<string name="phone_in_ecm_notification_complete_time" msgid="7341624337163082759">"<xliff:g id="COMPLETETIME">%s</xliff:g> болтол дата холболт байхгүй"</string>
<plurals name="alert_dialog_exit_ecm" formatted="false" msgid="5425906903766466743">
- <item quantity="other">Таны гар утас яаралтай түргэн тусламжийн дуудлагын хэлбэрт<xliff:g id="COUNT_1">%s</xliff:g> минут байна. Энэ горимд байгаа тохиолдолд интернет холболт ашигладаг ямар ч програмыг ашиглаж үл болно. Та гарахыг хүсэж байна уу?</item>
- <item quantity="one">Таны гар утас яаралтай түргэн тусламжийн дуудлагын хэлбэрт <xliff:g id="COUNT_0">%s</xliff:g> минут байна. Энэ горимд байгаа тохиолдолд интернет холболт ашигладаг ямар ч програмыг ашиглаж үл болно. Та гарахыг хүсэж байна уу?</item>
+ <item quantity="other">Таны гар утас яаралтай түргэн тусламжийн дуудлагын хэлбэрт<xliff:g id="COUNT_1">%s</xliff:g> минут байна. Энэ горимд байгаа тохиолдолд интернет холболт ашигладаг ямар ч программыг ашиглаж үл болно. Та гарахыг хүсэж байна уу?</item>
+ <item quantity="one">Таны гар утас яаралтай түргэн тусламжийн дуудлагын хэлбэрт <xliff:g id="COUNT_0">%s</xliff:g> минут байна. Энэ горимд байгаа тохиолдолд интернет холболт ашигладаг ямар ч программыг ашиглаж үл болно. Та гарахыг хүсэж байна уу?</item>
</plurals>
<plurals name="alert_dialog_not_avaialble_in_ecm" formatted="false" msgid="1152682528741457004">
<item quantity="other">Сонгосон үйлдлийг яаралтай түргэн тусламжийн горимд байгаа үед ашиглах боломжгүй. Таны гар утас энэ горимд <xliff:g id="COUNT_1">%s</xliff:g> минут байх болно. Та гарахыг хүсэж байна уу?</item>
@@ -649,7 +649,7 @@
<string name="description_dial_button" msgid="8614631902795087259">"залгах"</string>
<string name="description_dialpad_button" msgid="7395114120463883623">"дугаар цуглуулах самбарыг харуулах"</string>
<string name="pane_title_emergency_dialpad" msgid="3627372514638694401">"Яаралтай тусламжийн дугаар цуглуулах самбар"</string>
- <string name="voicemail_visual_voicemail_switch_title" msgid="6610414098912832120">"Уншиж болохуйц дуут шуудан"</string>
+ <string name="voicemail_visual_voicemail_switch_title" msgid="6610414098912832120">"Визуал дуут шуудан"</string>
<string name="voicemail_set_pin_dialog_title" msgid="7005128605986960003">"PIN тохируулах"</string>
<string name="voicemail_change_pin_dialog_title" msgid="4633077715231764435">"PIN өөрчлөх"</string>
<string name="preference_category_ringtone" msgid="8787281191375434976">"Хонхны ая & Чичиргээ"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Өгөгдмөл дата SIM-н SubId:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DLзурвасын өргөн (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL Зурвасын өргөн (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Үүрэн байршлын мэдээлэл (цуцалсан):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE Сувгийн бодит тохиргоо:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Үүрэн мэдээлэл сэргээх тариф:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Бүх үүрэн хэмжилтийн мэдээлэл:"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index a3fb35e..55c3fa2 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -121,7 +121,7 @@
<string name="labelCFNRc" msgid="4163399350778066013">"Jika tidak dapat dihubungi"</string>
<string name="messageCFNRc" msgid="6980340731313007250">"Nombor apabila tidak dapat dihubungi"</string>
<string name="sum_cfnrc_enabled" msgid="1799069234006073477">"Memajukan ke <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
- <string name="sum_cfnrc_disabled" msgid="739289696796917683">"Mati"</string>
+ <string name="sum_cfnrc_disabled" msgid="739289696796917683">"Dimatikan"</string>
<string name="disable_cfnrc_forbidden" msgid="775348748084726890">"Pembawa anda tidak menyokong pelumpuhan pemajuan panggilan semasa telefon anda tidak boleh dihubungi."</string>
<string name="updating_title" msgid="6130548922615719689">"Tetapan panggilan"</string>
<string name="call_settings_admin_user_only" msgid="7238947387649986286">"Tetapan panggilan hanya boleh diubah oleh pengguna pentadbir."</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SubId SIM data lalai:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Lebar Jalur DL (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Lebar Jalur UL (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Maklumat Lokasi Selular (ditamatkan):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Konfigurasi Saluran Fizikal LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Kadar Muat Semula Maklumat Selular:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Semua Maklumat Ukuran Selular:"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 91391c7..5eb579d 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -24,7 +24,7 @@
<string name="unknown" msgid="8279698889921830815">"မသိပါ"</string>
<string name="private_num" msgid="4487990167889159992">"လျို့ဝှက် နံပါတ်"</string>
<string name="payphone" msgid="7936735771836716941">"ငွေပေးရသည့်ဖုန်း"</string>
- <string name="onHold" msgid="6132725550015899006">"ခဏ ကိုင်ထားစဉ်"</string>
+ <string name="onHold" msgid="6132725550015899006">"ဖုန်းကိုင်ထားသည်"</string>
<string name="carrier_mmi_msg_title" msgid="6050165242447507034">"<xliff:g id="MMICARRIER">%s</xliff:g> မက်ဆေ့ဂျ်"</string>
<string name="default_carrier_mmi_msg_title" msgid="7754317179938537213">"ဖုန်းလိုင်းဝန်ဆောင်မှု မက်ဆေ့ဂျ်"</string>
<string name="mmiStarted" msgid="9212975136944568623">"MMIကုတ်နံပါတ်ကို စတင်ပြီးပါပြီ"</string>
@@ -74,7 +74,7 @@
<string name="phone_accounts_configure_account_settings" msgid="6622119715253196586">"အကောင့် ချိန်ညှိချက်များ ပြုပြင်မည်"</string>
<string name="phone_accounts_all_calling_accounts" msgid="1609600743500618823">"ခေါ်ဆိုနေသော အကောင့်များ အားလုံး"</string>
<string name="phone_accounts_all_calling_accounts_summary" msgid="2214134955430107240">"ဖုန်းခေါ်ဆိုနိုင်သည့် အကောင့်များအား ရွေးရန်"</string>
- <string name="wifi_calling" msgid="3650509202851355742">"Wi-Fi ခေါ်ဆိုမှု"</string>
+ <string name="wifi_calling" msgid="3650509202851355742">"Wi-Fi ခေါ်ဆိုခြင်း"</string>
<string name="connection_service_default_label" msgid="7332739049855715584">"တပ်ဆင်ပြီး ချိတ်ဆက်ရေး ဝန်ဆောင်မှု"</string>
<string name="voicemail" msgid="7697769412804195032">"အသံမေးလ်"</string>
<string name="voicemail_settings_with_label" msgid="4228431668214894138">"အသံမေးလ် ( <xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g> )"</string>
@@ -91,7 +91,7 @@
<string name="additional_cdma_call_settings" msgid="2178016561980611304">"ပိုမိုသော CDMA ခေါ်ဆိုမှု အပြင်အဆင်"</string>
<string name="sum_cdma_call_settings" msgid="3185825305136993636">"CDMA ခေါ်ဆိုမှုအတွက်သာ ဖြည့်စွက်အပြင်အဆင်"</string>
<string name="labelNwService" msgid="6015891883487125120">"ကွန်ရက် ဝန်ဆောင်မှု ဆက်တင်"</string>
- <string name="labelCallerId" msgid="2090540744550903172">"ခေါ်ဆိုသူအိုင်ဒီ"</string>
+ <string name="labelCallerId" msgid="2090540744550903172">"ခေါ်ဆိုသူ ID"</string>
<string name="sum_loading_settings" msgid="434063780286688775">"ဆက်တင်များကို ကြည့်ရှုရန် လုပ်နေပါသည်…"</string>
<string name="sum_hide_caller_id" msgid="131100328602371933">"အထွက်ဖုန်းများတွင် နံပါတ်ဖျောက်ထားပါ"</string>
<string name="sum_show_caller_id" msgid="3571854755324664591">"အထွက်ဖုန်းများတွင် နံပါတ်မြင်ရသည်"</string>
@@ -105,7 +105,7 @@
<string name="labelCFU" msgid="8870170873036279706">"အမြဲတမ်း ထပ်ဆင့်ပို့ပါ"</string>
<string name="messageCFU" msgid="1361806450979589744">"ဤနံပါတ်ကို အမြဲသုံးပါ"</string>
<string name="sum_cfu_enabled_indicator" msgid="9030139213402432776">"ခေါ်ဆိုမှုအားလုံးအား တဆင့်ထပ်ပို့နေသည်"</string>
- <string name="sum_cfu_enabled" msgid="5806923046528144526">"<xliff:g id="PHONENUMBER">{0}</xliff:g> သို့ခေါ်ဆိုမှုအားလုံးအား တဆင့်ထပ်ပို့နေသည်"</string>
+ <string name="sum_cfu_enabled" msgid="5806923046528144526">"ခေါ်ဆိုမှုအားလုံးကို <xliff:g id="PHONENUMBER">{0}</xliff:g> သို့ ထပ်ဆင့်ပို့နေသည်"</string>
<string name="sum_cfu_enabled_no_number" msgid="7287752761743377930">"ဖုန်းနံပါတ်မှာ မရှိပါ"</string>
<string name="sum_cfu_disabled" msgid="5010617134210809853">"ပိတ်ထားသည်"</string>
<string name="labelCFB" msgid="615265213360512768">"မအားလပ်ချိန်"</string>
@@ -127,7 +127,7 @@
<string name="call_settings_admin_user_only" msgid="7238947387649986286">"ခေါ်ဆိုမှုကြိုတင်ပြင်ဆင်ချက်များကို ကြီးကြပ်သူသာလျှင် ပြောင်းလဲနိုင်သည်။"</string>
<string name="call_settings_with_label" msgid="8460230435361579511">"ဆက်တင်များ ( <xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g> )"</string>
<string name="error_updating_title" msgid="2024290892676808965">"ခေါ်ဆိုမှုဆက်တင်အမှား"</string>
- <string name="reading_settings" msgid="1605904432450871183">"အပြင်အဆင်များကို ဖတ်နေပါသည်…"</string>
+ <string name="reading_settings" msgid="1605904432450871183">"ဆက်တင်များကို ဖတ်နေပါသည်…"</string>
<string name="updating_settings" msgid="3650396734816028808">"ဆက်တင်များအား ပြင်နေသည်…"</string>
<string name="reverting_settings" msgid="7378668837291012205">"ဆက်တင်များကို ပြန်ပြောင်းနေစဉ်…"</string>
<string name="response_error" msgid="3904481964024543330">"ကွန်ယက်မှ မထင်မှတ်သောတုံ့ပြန်ချက်"</string>
@@ -140,7 +140,7 @@
<string name="radio_off_error" msgid="8321564164914232181">"ဤအပြင်အဆင်များကို မကြည့်ခင် ရေဒီယိုကို ဖွင့်ပါ"</string>
<string name="close_dialog" msgid="1074977476136119408">"OK"</string>
<string name="enable" msgid="2636552299455477603">"ဖွင့်ထားရန်"</string>
- <string name="disable" msgid="1122698860799462116">"ပိတ်မည်"</string>
+ <string name="disable" msgid="1122698860799462116">"ပိတ်ရန်"</string>
<string name="change_num" msgid="6982164494063109334">"အဆင်မြှင့်ခြင်း"</string>
<string-array name="clir_display_values">
<item msgid="8477364191403806960">"မူရင်း ကွန်ရက်"</item>
@@ -513,7 +513,7 @@
<string name="card_title_conf_call" msgid="901197309274457427">"ကွန်းဖရင့်ခေါ်ဆိုမှု"</string>
<string name="card_title_incoming_call" msgid="881424648458792430">"အဝင်ခေါ်ဆိုမှု"</string>
<string name="card_title_call_ended" msgid="650223980095026340">"ဖုန်းခေါ်ဆိုမှု ပြီးဆုံးပါပြီ"</string>
- <string name="card_title_on_hold" msgid="9028319436626975207">"ခဏ ကိုင်ထားစဉ်"</string>
+ <string name="card_title_on_hold" msgid="9028319436626975207">"ဖုန်းကိုင်ထားသည်"</string>
<string name="card_title_hanging_up" msgid="814874106866647871">"ဖုန်းချနေပါသည်"</string>
<string name="card_title_in_call" msgid="8231896539567594265">"ဖုန်းပြောနေစဉ်"</string>
<string name="notification_voicemail_title" msgid="3932876181831601351">"အသံမေးလ်အသစ်"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"မူရင်း ဒေတာဆင်းမ်ကဒ်အတွက် Id အခွဲ −"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL မြန်နှုန်း (kbps)−"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL မြန်နှုန်း (kbps)−"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"ဆဲလ်လူလာတည်နေရာ အချက်အလက် (ကန့်ကွက်ထားသည်) −"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE ရုပ်ပိုင်းဆိုင်ရာ ချန်နယ်စီစဉ်သတ်မှတ်မှု−"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"ဆဲလ်လူလာ အချက်အလက် ရယူမှုနှုန်း −"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"ဆဲလ်လူလာတိုင်းတာမှု အချက်အလက် အားလုံး −"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 7b891fc..5b83aaf 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -141,7 +141,7 @@
<string name="close_dialog" msgid="1074977476136119408">"ठिक छ"</string>
<string name="enable" msgid="2636552299455477603">"सकृय पार्नुहोस्"</string>
<string name="disable" msgid="1122698860799462116">"निस्कृय पार्नुहोस्"</string>
- <string name="change_num" msgid="6982164494063109334">"अद्यावधिक गर्नुहोस्"</string>
+ <string name="change_num" msgid="6982164494063109334">"अपडेट गर्नुहोस्"</string>
<string-array name="clir_display_values">
<item msgid="8477364191403806960">"नेटवर्क पूर्वनिर्धारित"</item>
<item msgid="6813323051965618926">"सङ्ख्या लुकाउनुहोस्"</item>
@@ -298,7 +298,7 @@
<string name="sim_selection_required_pref" msgid="6985901872978341314">"चयन गर्न आवश्यक छ"</string>
<string name="sim_change_data_title" msgid="9142726786345906606">"डेटा सिम परिवर्तन गर्ने हो?"</string>
<string name="sim_change_data_message" msgid="3567358694255933280">"मोबाइल डेटाका लागि <xliff:g id="OLD_SIM">%2$s</xliff:g> को सट्टा <xliff:g id="NEW_SIM">%1$s</xliff:g> को प्रयोग गर्ने हो?"</string>
- <string name="wifi_calling_settings_title" msgid="5800018845662016507">"Wi-Fi कल"</string>
+ <string name="wifi_calling_settings_title" msgid="5800018845662016507">"Wi-Fi कलिङ"</string>
<string name="video_calling_settings_title" msgid="342829454913266078">"सेवा प्रदायकको भिडियो कल"</string>
<string name="gsm_umts_options" msgid="4968446771519376808">"GSM/UMTS विकल्पहरू"</string>
<string name="cdma_options" msgid="3669592472226145665">"CDMA विकल्पहरू"</string>
@@ -623,7 +623,7 @@
<string name="phone_entered_ecm_text" msgid="8431238297843035842">"आपतकालीन कलब्याक मोड प्रविष्टि गरियो"</string>
<string name="phone_in_ecm_notification_title" msgid="6825016389926367946">"आपतकालीन कलब्याक मोड"</string>
<string name="phone_in_ecm_call_notification_text" msgid="653972232922670335">"डेटा जडान अक्षम भयो"</string>
- <string name="phone_in_ecm_notification_complete_time" msgid="7341624337163082759">"<xliff:g id="COMPLETETIME">%s</xliff:g> सम्म डेटा जडान भएको छैन"</string>
+ <string name="phone_in_ecm_notification_complete_time" msgid="7341624337163082759">"<xliff:g id="COMPLETETIME">%s</xliff:g> सम्म डेटा कनेक्ट भएको छैन"</string>
<plurals name="alert_dialog_exit_ecm" formatted="false" msgid="5425906903766466743">
<item quantity="other"> फोन आपतकालीन कलब्याक मोडमा <xliff:g id="COUNT_1">%s</xliff:g> मिनेटको लागि हुनेछ। यस मोडको अवस्थामा एक डेटा जडान प्रयोग गरेर कुनै पनि अनुप्रयोगहरू प्रयोग गर्न सकिँदैन। के तपाईं अहिले निस्कन चाहनुहुन्छ?</item>
<item quantity="one"> फोन आपतकालीन कलब्याक मोडमा <xliff:g id="COUNT_0">%s</xliff:g> मिनेटको लागि हुनेछ। यस मोडको समयमा एक डेटा जडान प्रयोग गरेर कुनै पनि अनुप्रयोगहरू प्रयोग गर्न सकिँदैन। के तपाईं अहिले निस्कन चाहनुहुन्छ?</item>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"पूर्वनिर्धारित डेटा SIM को SubId:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL ब्यान्डविथ (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL व्यान्डविथ (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"सेलको स्थानबारे जानकारी (चल्तीबाट हटाइएको):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE को भौतिक च्यानलको कन्फिगरेसन:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"सेलसम्बन्धी जानकारीलाई पुनः ताजा गरिने दर:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"सेलको मापनसम्बन्धी सबै जानकारी:"</string>
@@ -888,7 +887,7 @@
<string name="radio_info_http_client_test" msgid="1329583721088428238">"HTTP क्लाइन्टको परीक्षण:"</string>
<string name="ping_test_label" msgid="448617502935719694">"पिङसम्बन्धी परीक्षण सञ्चालन गर्नुहोस्"</string>
<string name="radio_info_smsc_label" msgid="3749927072726033763">"SMSC:"</string>
- <string name="radio_info_smsc_update_label" msgid="5141996256097115753">"अद्यावधिक गर्नुहोस्"</string>
+ <string name="radio_info_smsc_update_label" msgid="5141996256097115753">"अपडेट गर्नुहोस्"</string>
<string name="radio_info_smsc_refresh_label" msgid="8409923721451604560">"पुनः ताजा गर्नुहोस्"</string>
<string name="radio_info_toggle_dns_check_label" msgid="1394078554927787350">"DNS को जाँचलाई टगल गर्नुहोस्"</string>
<string name="oem_radio_info_label" msgid="2914167475119997456">"OEM-विशिष्ट जानकारी/सेटिङ"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 5da9697..827bfce 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -32,7 +32,7 @@
<string name="mmiCancelled" msgid="5339191899200678272">"MMI-code geannuleerd"</string>
<string name="cancel" msgid="8984206397635155197">"Annuleren"</string>
<string name="enter_input" msgid="6193628663039958990">"USSD-bericht moet tussen <xliff:g id="MIN_LEN">%1$d</xliff:g> en <xliff:g id="MAX_LEN">%2$d</xliff:g> tekens lang zijn. Probeer het opnieuw."</string>
- <string name="manageConferenceLabel" msgid="8415044818156353233">"Telefonische vergadering beheren"</string>
+ <string name="manageConferenceLabel" msgid="8415044818156353233">"Conferencecall beheren"</string>
<string name="ok" msgid="7818974223666140165">"OK"</string>
<string name="audio_mode_speaker" msgid="243689733219312360">"Luidspreker"</string>
<string name="audio_mode_earpiece" msgid="2823700267171134282">"Oortelefoon van handset"</string>
@@ -506,11 +506,11 @@
<string name="pin2_attempts" msgid="5625178102026453023">\n"Je hebt nog <xliff:g id="NUMBER">%d</xliff:g> pogingen."</string>
<string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 niet langer geblokkeerd"</string>
<string name="pin2_error_exception" msgid="8116103864600823641">"Netwerk- of simkaartfout"</string>
- <string name="doneButton" msgid="7371209609238460207">"Gereed"</string>
+ <string name="doneButton" msgid="7371209609238460207">"Klaar"</string>
<string name="voicemail_settings_number_label" msgid="1265118640154688162">"Voicemailnummer"</string>
<string name="card_title_dialing" msgid="8742182654254431781">"Kiezen"</string>
<string name="card_title_redialing" msgid="18130232613559964">"Opnieuw bellen"</string>
- <string name="card_title_conf_call" msgid="901197309274457427">"Telefonische vergadering"</string>
+ <string name="card_title_conf_call" msgid="901197309274457427">"Conferencecall"</string>
<string name="card_title_incoming_call" msgid="881424648458792430">"Inkomend gesprek"</string>
<string name="card_title_call_ended" msgid="650223980095026340">"Gesprek beëindigd"</string>
<string name="card_title_on_hold" msgid="9028319436626975207">"In de wacht"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SubId van standaard simkaart voor data:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL-bandbreedte (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL-bandbreedte (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Mobiele locatiegegevens (beëindigd):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Fysieke LTE-kanaalconfiguratie:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Vernieuwingsfrequentie van mobiele data:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Alle mobiele meetgegevens:"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 3b2c3cd..65573ea 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"ଡିଫଲ୍ଟ ଡାଟା SIMର SubId:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL ବ୍ୟାଣ୍ଡୱିଡଥ୍ (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL ବ୍ୟାଣ୍ଡୱିଡଥ୍ (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"ସେଲ୍ ଲୋକେସନ୍ ସୂଚନା (ଅନୁରୋଧ କରାଯାଇଛି):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE ଫିଜିକାଲ୍ ଚ୍ୟାନେଲ୍ କନ୍ଫିଗରେସନ୍:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"ସେଲ୍ ସୂଚନା ରିଫ୍ରେସ୍ ଦର:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"ସମସ୍ତ ସେଲ୍ ପରିମାପ ସୂଚନା:"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 65df16f..aeecaa1 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"ਪੂਰਵ-ਨਿਰਧਾਰਤ ਡਾਟਾ ਸਿਮ ਦਾ SubId:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL ਬੈਂਡਵਿਡਥ (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL ਬੈਂਡਵਿਡਥ (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"ਸੈੱਲ ਦੀ ਟਿਕਾਣਾ ਜਾਣਕਾਰੀ (ਨਾਪਸੰਦ ਕੀਤੀ):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE ਭੌਤਿਕ ਚੈਨਲ ਸੰਰੂਪਣ:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"ਸੈੱਲ ਦੀ ਜਾਣਕਾਰੀ ਦੀ ਰਿਫ੍ਰੈਸ਼ ਦਰ:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"ਸਾਰੀ ਸੈੱਲ ਮਾਪ ਜਾਣਕਾਰੀ:"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 41b01ad..3abe83d 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -136,7 +136,7 @@
<string name="stk_cc_ss_to_ussd_error" msgid="8330749347425752192">"Pedido SS alterado para um novo pedido USSD"</string>
<string name="stk_cc_ss_to_ss_error" msgid="8297155544652134278">"Foi alterado para um novo pedido SS"</string>
<string name="stk_cc_ss_to_dial_video_error" msgid="4255261231466032505">"O pedido SS foi alterado para uma videochamada"</string>
- <string name="fdn_check_failure" msgid="1833769746374185247">"A definição Números Autorizados da aplicação do seu Telemóvel está ativada. Por conseguinte, algumas funcionalidades relacionadas com chamadas não estão a funcionar."</string>
+ <string name="fdn_check_failure" msgid="1833769746374185247">"A definição Números Autorizados da app do seu Telemóvel está ativada. Por conseguinte, algumas funcionalidades relacionadas com chamadas não estão a funcionar."</string>
<string name="radio_off_error" msgid="8321564164914232181">"Ative o rádio antes de visualizar estas definições."</string>
<string name="close_dialog" msgid="1074977476136119408">"OK"</string>
<string name="enable" msgid="2636552299455477603">"Ativar"</string>
@@ -286,7 +286,7 @@
<string name="limited_sim_function_notification_title" msgid="612715399099846281">"Funcionalidade de SIM limitada"</string>
<string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"As chamadas e os serviços de dados da <xliff:g id="CARRIER_NAME">%1$s</xliff:g> podem ficar bloqueados ao utilizar o número <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
<string name="limited_sim_function_notification_message" msgid="5338638075496721160">"As chamadas e serviços de dados da <xliff:g id="CARRIER_NAME">%1$s</xliff:g> podem ser bloqueados se utilizar outro SIM."</string>
- <string name="data_usage_title" msgid="8438592133893837464">"Utilização de dados da aplicação"</string>
+ <string name="data_usage_title" msgid="8438592133893837464">"Utilização de dados da app"</string>
<string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> de dados móveis utilizados entre <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="advanced_options_title" msgid="9208195294513520934">"Avançadas"</string>
<string name="carrier_settings_euicc" msgid="1190237227261337749">"Operador"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SubID do SIM de dados predefinido:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Largura de banda de transferência (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Largura de banda de carregamento (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Informações da localização da célula (descontinuadas):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Configuração do canal físico LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Taxa de atualização das informações da célula:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Todas as informações de medição de células:"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 3fd76d4..42d24e4 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -91,14 +91,14 @@
<string name="additional_cdma_call_settings" msgid="2178016561980611304">"Configurações de chamada CDMA adicionais"</string>
<string name="sum_cdma_call_settings" msgid="3185825305136993636">"Configurações adicionais somente de chamada CDMA"</string>
<string name="labelNwService" msgid="6015891883487125120">"Configurações do serviço de rede"</string>
- <string name="labelCallerId" msgid="2090540744550903172">"ID da chamada"</string>
+ <string name="labelCallerId" msgid="2090540744550903172">"Identificador de chamadas"</string>
<string name="sum_loading_settings" msgid="434063780286688775">"Carregando configurações…"</string>
<string name="sum_hide_caller_id" msgid="131100328602371933">"Número oculto nas chamadas enviadas"</string>
<string name="sum_show_caller_id" msgid="3571854755324664591">"Número exibido nas chamadas enviadas"</string>
- <string name="sum_default_caller_id" msgid="1767070797135682959">"Usar configurações padrão da operadora para exibir meu número em chamadas efetuadas"</string>
+ <string name="sum_default_caller_id" msgid="1767070797135682959">"Usar configurações padrão da operadora para exibir meu número em chamadas realizadas"</string>
<string name="labelCW" msgid="8449327023861428622">"Chamada em espera"</string>
- <string name="sum_cw_enabled" msgid="3977308526187139996">"Notificar sobre a entrada de outras chamadas durante uma ligação"</string>
- <string name="sum_cw_disabled" msgid="3658094589461768637">"Notificar sobre a entrada de outras chamadas durante uma ligação"</string>
+ <string name="sum_cw_enabled" msgid="3977308526187139996">"Notificar sobre outras chamadas recebidas durante uma ligação"</string>
+ <string name="sum_cw_disabled" msgid="3658094589461768637">"Notificar sobre outras chamadas recebidas durante uma ligação"</string>
<string name="call_forwarding_settings" msgid="8937130467468257671">"Configurações de encaminhamento de chamada"</string>
<string name="call_forwarding_settings_with_label" msgid="2345432813399564272">"Configurações de encaminhamento de chamada (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="labelCF" msgid="3578719437928476078">"Encaminhamento de chamada"</string>
@@ -136,7 +136,7 @@
<string name="stk_cc_ss_to_ussd_error" msgid="8330749347425752192">"Solicitação SS alterada para solicitação USSD"</string>
<string name="stk_cc_ss_to_ss_error" msgid="8297155544652134278">"Alterada para uma nova solicitação SS"</string>
<string name="stk_cc_ss_to_dial_video_error" msgid="4255261231466032505">"Solicitação SS alterada para videochamada"</string>
- <string name="fdn_check_failure" msgid="1833769746374185247">"A configuração \"Números de discagem fixa\" do seu app Telefone está ativada. Por isso, alguns recursos relacionados a chamadas não funcionam."</string>
+ <string name="fdn_check_failure" msgid="1833769746374185247">"A configuração \"N fixa\" do seu app Telefone está ativada. Por isso, alguns recursos relacionados a chamadas não funcionam."</string>
<string name="radio_off_error" msgid="8321564164914232181">"Ligue o rádio antes de ver essas configurações."</string>
<string name="close_dialog" msgid="1074977476136119408">"OK"</string>
<string name="enable" msgid="2636552299455477603">"Ativar"</string>
@@ -425,8 +425,8 @@
<string name="cdma_activate_device" msgid="5914720276140097632">"Ativar aparelho"</string>
<string name="cdma_lte_data_service" msgid="359786441782404562">"Configurar serviço de dados"</string>
<string name="carrier_settings_title" msgid="6292869148169850220">"Config. da operadora"</string>
- <string name="fdn" msgid="2545904344666098749">"Números de discagem fixa"</string>
- <string name="fdn_with_label" msgid="6412087553365709494">"Números de discagem fixa (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
+ <string name="fdn" msgid="2545904344666098749">"N fixa"</string>
+ <string name="fdn_with_label" msgid="6412087553365709494">"N fixa (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="manage_fdn_list" msgid="3341716430375195441">"Lista FDN"</string>
<string name="fdn_list_with_label" msgid="1409655283510382556">"Lista de FDNs (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="fdn_activation" msgid="2178637004710435895">"Ativação do FDN"</string>
@@ -510,7 +510,7 @@
<string name="voicemail_settings_number_label" msgid="1265118640154688162">"Número correio de voz"</string>
<string name="card_title_dialing" msgid="8742182654254431781">"Discando"</string>
<string name="card_title_redialing" msgid="18130232613559964">"Rediscando"</string>
- <string name="card_title_conf_call" msgid="901197309274457427">"teleconferência"</string>
+ <string name="card_title_conf_call" msgid="901197309274457427">"Teleconferência"</string>
<string name="card_title_incoming_call" msgid="881424648458792430">"Recebendo chamada"</string>
<string name="card_title_call_ended" msgid="650223980095026340">"Chamada encerrada"</string>
<string name="card_title_on_hold" msgid="9028319436626975207">"Em espera"</string>
@@ -554,7 +554,7 @@
<string name="emergency_enable_radio_dialog_retry" msgid="4329131876852608587">"Sem serviço. Tentando novamente..."</string>
<string name="radio_off_during_emergency_call" msgid="8011154134040481609">"Não é possível usar o modo avião durante uma chamada de emergência."</string>
<string name="dial_emergency_error" msgid="825822413209026039">"Não é possível realizar chamadas. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> não é um telefone de emergência."</string>
- <string name="dial_emergency_empty_error" msgid="2785803395047793634">"Não é possível realizar chamadas. Disque o número de telefone de emergência."</string>
+ <string name="dial_emergency_empty_error" msgid="2785803395047793634">"Não é possível realizar chamadas. Ligue para o número de telefone de emergência."</string>
<string name="dial_emergency_calling_not_available" msgid="6485846193794727823">"Chamada de emergência indisponível"</string>
<string name="pin_puk_system_user_only" msgid="1045147220686867922">"Somente o proprietário do dispositivo pode digitar os códigos PIN/PUK."</string>
<string name="police_type_description" msgid="2819533883972081757">"Polícia"</string>
@@ -572,7 +572,7 @@
<string name="onscreenManageCallsText" msgid="1162047856081836469">"Gerenciar chamadas"</string>
<string name="onscreenManageConferenceText" msgid="4700574060601755137">"Gerenciar conferência"</string>
<string name="onscreenAudioText" msgid="7224226735052019986">"Áudio"</string>
- <string name="onscreenVideoCallText" msgid="1743992456126258698">"Videocham."</string>
+ <string name="onscreenVideoCallText" msgid="1743992456126258698">"Vídeo"</string>
<string name="importSimEntry" msgid="3892354284082689894">"Importar"</string>
<string name="importAllSimEntries" msgid="2628391505643564007">"Importar tudo"</string>
<string name="importingSimContacts" msgid="4995457122107888932">"Importando contatos do chip"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Subcódigo do chip de dados padrão:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Largura de banda DL (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Largura de banda UL (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Informações de local do celular (uso suspenso):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Configuração do canal físico de LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Taxa de atualização das informações do celular:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Todas as informações de medição do celular:"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 1b1f1b7..bf0f4b1 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -863,7 +863,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SubId pentru SIM-ul de date prestabilit:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Lățime de bandă de descărcare (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Lățime de bandă de încărcare (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Informații despre localizarea celulei (învechite):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Configurarea canalului fizic LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Rata de actualizare a informațiilor despre celulă:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Toate informațiile de măsurare despre celulă:"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 8086351..5ed5d49 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -96,7 +96,7 @@
<string name="sum_hide_caller_id" msgid="131100328602371933">"Скрывать номер при исходящих вызовах"</string>
<string name="sum_show_caller_id" msgid="3571854755324664591">"Номер, отображающийся при исходящих вызовах"</string>
<string name="sum_default_caller_id" msgid="1767070797135682959">"Использовать стандартные настройки оператора для показа моего номера при исходящих вызовах"</string>
- <string name="labelCW" msgid="8449327023861428622">"Параллельный вызов"</string>
+ <string name="labelCW" msgid="8449327023861428622">"Ожидание вызова"</string>
<string name="sum_cw_enabled" msgid="3977308526187139996">"Извещать меня о входящих вызовах во время разговора"</string>
<string name="sum_cw_disabled" msgid="3658094589461768637">"Извещать меня о входящих вызовах во время разговора"</string>
<string name="call_forwarding_settings" msgid="8937130467468257671">"Переадресация"</string>
@@ -867,7 +867,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Доп. идентификатор SIM-карты для мобильного Интернета по умолчанию:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Пропускная способность DL-канала (кбит/c):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Пропускная способность UL-канала (кбит/с):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Информация о местоположении сети (устаревшее):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Конфигурация физического канала LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Частота обновления данных о сетях:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Статистика сети:"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index e92c865..797a455 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"පෙරනිමි දත්ත SIM පතේ උප හැඳුනුම:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL කලාප පළල (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL කලාප පළල (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"සෙල් ස්ථාන තොරතුරු (අත් හරින ලදී):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE භෞතික නාලිකා වින්යාසය:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"සෙල් තොරතුරු නැවුම් කිරීමේ අනුපාතය:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"සියලු සෙල් මිනුම් තොරතුරු:"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index e38b635..a42585b 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -127,7 +127,7 @@
<string name="call_settings_admin_user_only" msgid="7238947387649986286">"Nastavenia hovorov môže zmeniť iba používateľ s povoleniami správcu."</string>
<string name="call_settings_with_label" msgid="8460230435361579511">"Nastavenia (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="error_updating_title" msgid="2024290892676808965">"Chyba nastavení hovorov"</string>
- <string name="reading_settings" msgid="1605904432450871183">"Nastavenia sa načítajú…"</string>
+ <string name="reading_settings" msgid="1605904432450871183">"Nastavenia sa načítavajú…"</string>
<string name="updating_settings" msgid="3650396734816028808">"Prebieha aktualizácia nastavení..."</string>
<string name="reverting_settings" msgid="7378668837291012205">"Prebieha vrátenie predchádzajúcich nastavení…"</string>
<string name="response_error" msgid="3904481964024543330">"Neočakávaná odpoveď siete."</string>
@@ -865,7 +865,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Podradený identifikátor predvolenej dátovej SIM karty:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Rýchlosť pripojenia DL (kB/s):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Rýchlosť pripojenia UL (kB/s):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Informácie o polohe mobilnej siete (podpora bola ukončená):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Konfigurácia fyzického kanála LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Frekvencia obnovenia informácií o mobilnej sieti:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Všetky informácie o meraní mobilnej siete:"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 0ee96d6..a44af48 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -865,7 +865,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"ID naročnine privzete kartice SIM za prenos podatkov:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Pasovna širina za prenos (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Pasovna širina za nalaganje (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Podatki o lokaciji celice (zastarelo):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Konfiguracija fizičnega kanala LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Frekvenca osveževanja podatkov o celici:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Vsi podatki o meritvah celice:"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 69a24f1..362fb8a 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -118,7 +118,7 @@
<string name="sum_cfnry_enabled" msgid="3000500837493854799">"Прослеђује се на <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
<string name="sum_cfnry_disabled" msgid="1990563512406017880">"Искључено"</string>
<string name="disable_cfnry_forbidden" msgid="3174731413216550689">"Оператер не подржава онемогућавање преусмеравања позива када се на позив не одговори."</string>
- <string name="labelCFNRc" msgid="4163399350778066013">"Кад сам недоступан/а"</string>
+ <string name="labelCFNRc" msgid="4163399350778066013">"Кад сам недоступан/на"</string>
<string name="messageCFNRc" msgid="6980340731313007250">"Број кад је недоступно"</string>
<string name="sum_cfnrc_enabled" msgid="1799069234006073477">"Прослеђује се на <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
<string name="sum_cfnrc_disabled" msgid="739289696796917683">"Искључено"</string>
@@ -863,7 +863,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SubID подразумеваног SIM-а за податке:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL пропусни опсег (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL пропусни опсег (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Информације о локацији предајника (застарело):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Конфигурација LTE физичког канала:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Учесталост освежавања информација о предајнику:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Све информације о мерењу за предајник:"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 6ff09d6..896d981 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -686,7 +686,7 @@
<string name="change_pin_enter_old_pin_hint" msgid="8801292976275169367">"Ange pinkoden till röstbrevlådan för att fortsätta."</string>
<string name="change_pin_enter_new_pin_header" msgid="4739465616733486118">"Ange en ny pinkod"</string>
<string name="change_pin_enter_new_pin_hint" msgid="2326038476516364210">"Pinkoden måste ha <xliff:g id="MIN">%1$d</xliff:g>-<xliff:g id="MAX">%2$d</xliff:g> siffror."</string>
- <string name="change_pin_confirm_pin_header" msgid="2606303906320705726">"Bekräfta pinkod"</string>
+ <string name="change_pin_confirm_pin_header" msgid="2606303906320705726">"Bekräfta PIN-kod"</string>
<string name="change_pin_confirm_pins_dont_match" msgid="305164501222587215">"Pinkoderna matchar inte"</string>
<string name="change_pin_succeeded" msgid="2504705600693014403">"Röstbrevlådans pinkod bekräftad"</string>
<string name="change_pin_system_error" msgid="7772788809875146873">"Det går inte att ställa in pinkod"</string>
@@ -865,7 +865,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SubId för standarddata på SIM-kortet:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Bandbredd för nedladdning (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Bandbredd för uppladdning (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Platsinformation för mast (utfasad):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Fysisk kanalkonfiguration för LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Frekvens för uppdatering av mastinformation:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Alla information om mastmätning:"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 8382af1..3d1f1cb 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"இயல்பான டேட்டா சிம்மின் துணை ஐடி:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL இணைய வேகம் (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL இணைய வேகம் (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"மொபைலின் இருப்பிடத் தகவல் (நிறுத்தப்பட்டது):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE ஃபிசிக்கல் சேனல் உள்ளமைவு:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"மொபைல் தகவலின் புதுப்பிப்பு விகிதம்:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"அனைத்து மொபைல் அளவீட்டுத் தகவல்:"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index a4f1660..3c0d2e6 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -128,7 +128,7 @@
<string name="call_settings_with_label" msgid="8460230435361579511">"సెట్టింగ్లు (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="error_updating_title" msgid="2024290892676808965">"కాల్ సెట్టింగ్ల లోపం"</string>
<string name="reading_settings" msgid="1605904432450871183">"సెట్టింగ్లను చదువుతోంది…"</string>
- <string name="updating_settings" msgid="3650396734816028808">"సెట్టింగ్లను నవీకరిస్తోంది…"</string>
+ <string name="updating_settings" msgid="3650396734816028808">"సెట్టింగ్లను అప్డేట్ చేస్తోంది…"</string>
<string name="reverting_settings" msgid="7378668837291012205">"సెట్టింగ్లను తిరిగి మారుస్తోంది…"</string>
<string name="response_error" msgid="3904481964024543330">"నెట్వర్క్ నుండి ఊహించని ప్రతిస్పందన."</string>
<string name="exception_error" msgid="330994460090467">"నెట్వర్క్ లేదా SIM కార్డు లోపం."</string>
@@ -141,7 +141,7 @@
<string name="close_dialog" msgid="1074977476136119408">"సరే"</string>
<string name="enable" msgid="2636552299455477603">"ఆన్ చేయి"</string>
<string name="disable" msgid="1122698860799462116">"ఆఫ్ చేయి"</string>
- <string name="change_num" msgid="6982164494063109334">"నవీకరించు"</string>
+ <string name="change_num" msgid="6982164494063109334">"అప్డేట్ చేయి"</string>
<string-array name="clir_display_values">
<item msgid="8477364191403806960">"నెట్వర్క్ డిఫాల్ట్"</item>
<item msgid="6813323051965618926">"నంబర్ను దాచు"</item>
@@ -581,7 +581,7 @@
<string name="failedToImportSingleContactMsg" msgid="228095510489830266">"పరిచయాన్ని దిగుమతి చేయడంలో విఫలమైంది"</string>
<string name="hac_mode_title" msgid="4127986689621125468">"వినికిడి సహాయక సాధనాలు"</string>
<string name="hac_mode_summary" msgid="7774989500136009881">"వినికిడి సహాయక సాధనం అనుకూలతను ప్రారంభించండి"</string>
- <string name="rtt_mode_title" msgid="3075948111362818043">"నిజ సమయ వచనం (RTT) కాల్"</string>
+ <string name="rtt_mode_title" msgid="3075948111362818043">"రియల్-టైమ్ టెక్స్ట్ (RTT) కాల్"</string>
<string name="rtt_mode_summary" msgid="8631541375609989562">"వాయిస్ కాల్లో సందేశాలు పంపడానికి అనుమతించండి"</string>
<string name="rtt_mode_more_information" msgid="587500128658756318">"బధిరులు, వినికిడి సమస్యలు ఉన్న వారు, మాట్లాడటంలో సమస్యలు ఉన్న వారు లేదా కేవలం వాయిస్తో అర్థం చేసుకోలేని కాలర్లకు RTT సహాయపడుతుంది.<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>మరింత తెలుసుకోండి</a>\n <br><br> - RTT కాల్లు సందేశ లిపి మార్పు వలె సేవ్ చేయబడతాయి\n <br> - వీడియో కాల్ల కోసం RTT అందుబాటులో లేదు"</string>
<string name="no_rtt_when_roaming" msgid="5268008247378355389">"గమనిక: రోమింగ్లో ఉండగా RTTఅందుబాటులో ఉండదు"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"డిఫాల్ట్ డేటా SIM యొక్క SubId:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL బ్యాండ్విడ్త్ (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL బ్యాండ్విడ్త్ (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"సెల్ స్థాన సమాచారం (నిలిపివేయబడింది):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE భౌతిక ఛానెల్ కాన్ఫిగరేషన్:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"సెల్ సమాచార రిఫ్రెష్ సగటు:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"మొత్తం సెల్ పరిమాణ సమాచారం:"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index e5eee82..e146f30 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -425,8 +425,8 @@
<string name="cdma_activate_device" msgid="5914720276140097632">"เปิดใช้งานอุปกรณ์"</string>
<string name="cdma_lte_data_service" msgid="359786441782404562">"ตั้งค่าบริการข้อมูล"</string>
<string name="carrier_settings_title" msgid="6292869148169850220">"การตั้งค่าของผู้ให้บริการ"</string>
- <string name="fdn" msgid="2545904344666098749">"การจำกัดหมายเลขโทรออก"</string>
- <string name="fdn_with_label" msgid="6412087553365709494">"โหมดจำกัดการโทร (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
+ <string name="fdn" msgid="2545904344666098749">"หมายเลขโทรออกที่กำหนดตายตัว"</string>
+ <string name="fdn_with_label" msgid="6412087553365709494">"หมายเลขโทรออกที่กำหนดตายตัว (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="manage_fdn_list" msgid="3341716430375195441">"รายการ FDN"</string>
<string name="fdn_list_with_label" msgid="1409655283510382556">"รายการ FDN (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="fdn_activation" msgid="2178637004710435895">"การเปิดใช้งาน FDN"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"SubId ของซิมอินเทอร์เน็ตเริ่มต้น:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"แบนด์วิดท์ดาวน์โหลด (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"แบนด์วิดท์อัปโหลด (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"ข้อมูลตำแหน่งของเครือข่ายมือถือ (เลิกใช้งานแล้ว):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"การกำหนดค่าแชเนลทางกายภาพของ LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"อัตราการรีเฟรชข้อมูลมือถือ:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"ข้อมูลการวัดเครือข่ายมือถือทั้งหมด:"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 05412b1..dfff35d 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Varsayılan veri SIM\'inin alt kimliği:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"İndirme Bant Genişliği (kb/sn.):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Yükleme Bant Genişliği (kb/sn.):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Hücre Konumu Bilgisi (kullanımdan kaldırıldı):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE Fiziksel Kanal Yapılandırması:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Hücre Bilgilerini Yenileme Hızı:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Tüm Hücre Ölçümü Bilgileri:"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index ff4cf88..673e01f 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -865,7 +865,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Субідентифікатор SIM-карти для даних за умовчанням:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Пропускна спроможність DL (кбіт/с):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Пропускна спроможність UL (кбіт/с):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Геодані телефона (більше не підтримуються):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Конфігурація фізичного каналу LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Частота оновлення даних про мобільний зв\'язок:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Усі дані про показники мобільного зв\'язку:"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 9cfeaa3..d3885cb 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -95,17 +95,17 @@
<string name="sum_loading_settings" msgid="434063780286688775">"ترتیبات لوڈ ہو رہی ہیں…"</string>
<string name="sum_hide_caller_id" msgid="131100328602371933">"باہر جانے والی کالوں میں نمبر پوشیدہ ہے"</string>
<string name="sum_show_caller_id" msgid="3571854755324664591">"باہر جانے والی کالوں میں ڈسپلے نمبر ہوتا ہے"</string>
- <string name="sum_default_caller_id" msgid="1767070797135682959">"باہر جانے والی کالوں میں میرا نمبر ڈسپلے کرنے کیلئے ڈیفالٹ آپریٹر کی ترتیبات کا استعمال کریں"</string>
+ <string name="sum_default_caller_id" msgid="1767070797135682959">"آؤٹ گوئنگ کالوں میں میرا نمبر ڈسپلے کرنے کیلئے ڈیفالٹ آپریٹر کی ترتیبات کا استعمال کریں"</string>
<string name="labelCW" msgid="8449327023861428622">"کال کا انتظار"</string>
- <string name="sum_cw_enabled" msgid="3977308526187139996">"کال کے دوران، مجھے آنے والی کالوں سے مطلع کریں"</string>
- <string name="sum_cw_disabled" msgid="3658094589461768637">"کال کے دوران، مجھے آنے والی کالوں سے مطلع کریں"</string>
- <string name="call_forwarding_settings" msgid="8937130467468257671">"کال آگے منتقل کرنے کی ترتیبات"</string>
+ <string name="sum_cw_enabled" msgid="3977308526187139996">"کال کے دوران، مجھے اِن کمنگ کالوں سے مطلع کریں"</string>
+ <string name="sum_cw_disabled" msgid="3658094589461768637">"کال کے دوران، مجھے اِن کمنگ کالوں سے مطلع کریں"</string>
+ <string name="call_forwarding_settings" msgid="8937130467468257671">"کال فارورڈنگ کی ترتیبات"</string>
<string name="call_forwarding_settings_with_label" msgid="2345432813399564272">"کال فارورڈنگ ترتیبات (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="labelCF" msgid="3578719437928476078">"کال فارورڈنگ"</string>
- <string name="labelCFU" msgid="8870170873036279706">"ہمیشہ آگے منتقل کریں"</string>
+ <string name="labelCFU" msgid="8870170873036279706">"ہمیشہ فارورڈ کریں"</string>
<string name="messageCFU" msgid="1361806450979589744">"ہمیشہ یہ نمبر استعمال کریں"</string>
<string name="sum_cfu_enabled_indicator" msgid="9030139213402432776">"سبھی کالیں آگے منتقل کر رہا ہے"</string>
- <string name="sum_cfu_enabled" msgid="5806923046528144526">"سبھی کالیں <xliff:g id="PHONENUMBER">{0}</xliff:g> کو آگے منتقل کر رہا ہے"</string>
+ <string name="sum_cfu_enabled" msgid="5806923046528144526">"سبھی کالیں <xliff:g id="PHONENUMBER">{0}</xliff:g> کو فارورڈ ہو رہی ہیں"</string>
<string name="sum_cfu_enabled_no_number" msgid="7287752761743377930">"نمبر دستیاب نہیں ہے"</string>
<string name="sum_cfu_disabled" msgid="5010617134210809853">"آف"</string>
<string name="labelCFB" msgid="615265213360512768">"مصروف ہونے پر"</string>
@@ -511,7 +511,7 @@
<string name="card_title_dialing" msgid="8742182654254431781">"ڈائل ہو رہا ہے"</string>
<string name="card_title_redialing" msgid="18130232613559964">"دوبارہ ڈائل ہو رہا ہے"</string>
<string name="card_title_conf_call" msgid="901197309274457427">"کانفرنس کال"</string>
- <string name="card_title_incoming_call" msgid="881424648458792430">"آنے والی کال"</string>
+ <string name="card_title_incoming_call" msgid="881424648458792430">"اِن کمنگ کال"</string>
<string name="card_title_call_ended" msgid="650223980095026340">"کال ختم ہوگئی"</string>
<string name="card_title_on_hold" msgid="9028319436626975207">"ہولڈ پر"</string>
<string name="card_title_hanging_up" msgid="814874106866647871">"کال منقطع ہو رہی ہے"</string>
@@ -806,7 +806,7 @@
<string name="supp_service_held_call_released" msgid="2847835124639112410">"ہولڈ پر رکھی کال جاری ہو گئی۔"</string>
<string name="callFailed_otasp_provisioning_in_process" msgid="3345666183602879326">"کال نہیں کی جا سکتی کیوںکہ فی الحال آلہ فراہم کیا جا رہا ہے۔"</string>
<string name="callFailed_already_dialing" msgid="7250591188960691086">"کال نہیں کی جا سکتی کیونکہ باہر جانے والی دوسری کال پہلے سے ڈائل کی جا رہی ہے۔"</string>
- <string name="callFailed_already_ringing" msgid="2376603543544289303">"ایک جواب نہ ملنے والی موصول ہونے والی کال کی وجہ سے کال نہیں کی جا سکتی۔ نئی کال کرنے کے لیے پہلے موصول ہونے والی کال کا جواب دیں یا مسترد کریں۔"</string>
+ <string name="callFailed_already_ringing" msgid="2376603543544289303">"ایک جواب نہ ملنے والی اِن کمنگ کال کی وجہ سے کال نہیں کی جا سکتی۔ نئی کال کرنے کے لیے پہلے اِن کمنگ کال کا جواب دیں یا مسترد کریں۔"</string>
<string name="callFailed_calling_disabled" msgid="5010992739401206283">"ro.telephony.disable-call سسٹم کی خصوصیت کے ذریعے کال کرنے کو غیر فعال کر دیے جانے کی وجہ سے کال نہیں کی جا سکتی۔"</string>
<string name="callFailed_too_many_calls" msgid="5379426826618582180">"پہلے سے دو کالز کے پیش رفت میں ہونے کی وجہ سے کال نہیں کی جا سکتی۔ نئی کال کرنے کے لیے پہلے ان میں سے ایک کو غیر منسلک کریں یا انہیں کانفرنس میں ضم کریں۔"</string>
<string name="supp_service_over_ut_precautions" msgid="2145018231396701311">"<xliff:g id="SUPP_SERVICE">%s</xliff:g> استعمال کرنے کے لیے، یقینی بنائيں کہ موبائل ڈیٹا آن ہے۔ آپ موبائل یٹ ورک کی ترتیبات میں اسے تبدیل کر سکتے ہیں۔"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"ڈیفالٹ ڈیٹا SIM کی SubId:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL بینڈ وڈتھ (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL بینڈ وڈتھ (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"سیل کے مقام کی معلومات (فرسودہ):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE فزيکل چینل کنفیگریشن:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"سیل کی معلومات ریفریش کرنے کی شرح:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"سیل پیمائش کی تمام معلومات:"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 371d0da..6598431 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -24,7 +24,7 @@
<string name="unknown" msgid="8279698889921830815">"Không xác định"</string>
<string name="private_num" msgid="4487990167889159992">"Số cá nhân"</string>
<string name="payphone" msgid="7936735771836716941">"Điện thoại công cộng"</string>
- <string name="onHold" msgid="6132725550015899006">"Đang chờ"</string>
+ <string name="onHold" msgid="6132725550015899006">"Đang giữ máy"</string>
<string name="carrier_mmi_msg_title" msgid="6050165242447507034">"Tin nhắn <xliff:g id="MMICARRIER">%s</xliff:g>"</string>
<string name="default_carrier_mmi_msg_title" msgid="7754317179938537213">"Tin nhắn nhà mạng"</string>
<string name="mmiStarted" msgid="9212975136944568623">"Đã bắt đầu mã MMI"</string>
@@ -85,13 +85,13 @@
<string name="voicemail_notifications_preference_title" msgid="7829238858063382977">"Thông báo"</string>
<string name="cell_broadcast_settings" msgid="8135324242541809924">"Phát sóng trong tình huống khẩn cấp"</string>
<string name="call_settings" msgid="3677282690157603818">"Cài đặt cuộc gọi"</string>
- <string name="additional_gsm_call_settings" msgid="1561980168685658846">"Cài đặt bổ sung"</string>
- <string name="additional_gsm_call_settings_with_label" msgid="7973920539979524908">"Cài đặt bổ sung (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
+ <string name="additional_gsm_call_settings" msgid="1561980168685658846">"Tùy chọn cài đặt bổ sung"</string>
+ <string name="additional_gsm_call_settings_with_label" msgid="7973920539979524908">"Tùy chọn cài đặt bổ sung (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="sum_gsm_call_settings" msgid="7964692601608878138">"Cài đặt cuộc gọi chỉ GSM bổ sung"</string>
<string name="additional_cdma_call_settings" msgid="2178016561980611304">"Cài đặt cuộc gọi CDMA bổ sung"</string>
<string name="sum_cdma_call_settings" msgid="3185825305136993636">"Cài đặt cuộc gọi chỉ CDMA bổ sung"</string>
<string name="labelNwService" msgid="6015891883487125120">"Cài đặt dịch vụ mạng"</string>
- <string name="labelCallerId" msgid="2090540744550903172">"ID người gọi"</string>
+ <string name="labelCallerId" msgid="2090540744550903172">"Số nhận dạng người gọi"</string>
<string name="sum_loading_settings" msgid="434063780286688775">"Đang tải cài đặt…"</string>
<string name="sum_hide_caller_id" msgid="131100328602371933">"Số bị ẩn trong cuộc gọi đi"</string>
<string name="sum_show_caller_id" msgid="3571854755324664591">"Số được hiển thị trong cuộc gọi đi"</string>
@@ -127,8 +127,8 @@
<string name="call_settings_admin_user_only" msgid="7238947387649986286">"Chỉ người dùng quản trị mới có thể thay đổi cài đặt cuộc gọi."</string>
<string name="call_settings_with_label" msgid="8460230435361579511">"Cài đặt (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="error_updating_title" msgid="2024290892676808965">"Lỗi cài đặt cuộc gọi"</string>
- <string name="reading_settings" msgid="1605904432450871183">"Đang đọc cài đặt…"</string>
- <string name="updating_settings" msgid="3650396734816028808">"Đang cập nhật cài đặt..."</string>
+ <string name="reading_settings" msgid="1605904432450871183">"Đang đọc các tùy chọn cài đặt…"</string>
+ <string name="updating_settings" msgid="3650396734816028808">"Đang cập nhật các tùy chọn cài đặt..."</string>
<string name="reverting_settings" msgid="7378668837291012205">"Đang hoàn nguyên cài đặt…"</string>
<string name="response_error" msgid="3904481964024543330">"Phản hồi không mong muốn từ mạng."</string>
<string name="exception_error" msgid="330994460090467">"Lỗi mạng hoặc thẻ SIM."</string>
@@ -299,7 +299,7 @@
<string name="sim_change_data_title" msgid="9142726786345906606">"Thay đổi SIM cho dữ liệu di động?"</string>
<string name="sim_change_data_message" msgid="3567358694255933280">"Sử dụng <xliff:g id="NEW_SIM">%1$s</xliff:g> thay vì <xliff:g id="OLD_SIM">%2$s</xliff:g> cho dữ liệu di động?"</string>
<string name="wifi_calling_settings_title" msgid="5800018845662016507">"Gọi qua Wi-Fi"</string>
- <string name="video_calling_settings_title" msgid="342829454913266078">"Tính năng gọi điện video của nhà cung cấp dịch vụ"</string>
+ <string name="video_calling_settings_title" msgid="342829454913266078">"Tính năng gọi video của nhà cung cấp dịch vụ"</string>
<string name="gsm_umts_options" msgid="4968446771519376808">"Tùy chọn GSM/UMTS"</string>
<string name="cdma_options" msgid="3669592472226145665">"Tùy chọn CDMA"</string>
<string name="throttle_data_usage" msgid="1944145350660420711">"Sử dụng dữ liệu"</string>
@@ -447,7 +447,7 @@
<string name="auto_retry_mode_title" msgid="2985801935424422340">"Tự động thử lại"</string>
<string name="auto_retry_mode_summary" msgid="2863919925349511402">"Bật chế độ Tự động thử lại"</string>
<string name="tty_mode_not_allowed_video_call" msgid="6551976083652752815">"Không được phép thay đổi Chế độ Máy điện báo đánh chữ (TTY) trong cuộc gọi video"</string>
- <string name="menu_add" msgid="5616487894975773141">"Thêm liên hệ"</string>
+ <string name="menu_add" msgid="5616487894975773141">"Thêm người liên hệ"</string>
<string name="menu_edit" msgid="3593856941552460706">"Chỉnh sửa liên hệ"</string>
<string name="menu_delete" msgid="6326861853830546488">"Xóa liên hệ"</string>
<string name="menu_dial" msgid="4178537318419450012">"Quay số liên hệ"</string>
@@ -513,7 +513,7 @@
<string name="card_title_conf_call" msgid="901197309274457427">"Cuộc gọi hội nghị"</string>
<string name="card_title_incoming_call" msgid="881424648458792430">"Cuộc gọi đến"</string>
<string name="card_title_call_ended" msgid="650223980095026340">"Cuộc gọi đã kết thúc"</string>
- <string name="card_title_on_hold" msgid="9028319436626975207">"Đang chờ"</string>
+ <string name="card_title_on_hold" msgid="9028319436626975207">"Đang giữ máy"</string>
<string name="card_title_hanging_up" msgid="814874106866647871">"Kết thúc cuộc gọi"</string>
<string name="card_title_in_call" msgid="8231896539567594265">"Đang trong cuộc gọi"</string>
<string name="notification_voicemail_title" msgid="3932876181831601351">"Thư thoại mới"</string>
@@ -654,8 +654,8 @@
<string name="voicemail_change_pin_dialog_title" msgid="4633077715231764435">"Thay đổi mã PIN"</string>
<string name="preference_category_ringtone" msgid="8787281191375434976">"Nhạc chuông và rung"</string>
<string name="pstn_connection_service_label" msgid="9200102709997537069">"Thẻ SIM tích hợp sẵn"</string>
- <string name="enable_video_calling_title" msgid="7246600931634161830">"Bật gọi điện video"</string>
- <string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"Để bật tính năng gọi điện video, bạn cần bật chế độ 4G LTE tăng cường trong cài đặt mạng."</string>
+ <string name="enable_video_calling_title" msgid="7246600931634161830">"Bật gọi video"</string>
+ <string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"Để bật tính năng gọi video, bạn cần bật chế độ 4G LTE tăng cường trong cài đặt mạng."</string>
<string name="enable_video_calling_dialog_settings" msgid="8697890611305307110">"Cài đặt mạng"</string>
<string name="enable_video_calling_dialog_close" msgid="4298929725917045270">"Đóng"</string>
<string name="sim_label_emergency_calls" msgid="9078241989421522310">"Cuộc gọi khẩn cấp"</string>
@@ -798,7 +798,7 @@
<string name="supp_service_additional_call_forwarded" msgid="8772753260008398632">"Đã chuyển tiếp cuộc gọi bổ sung."</string>
<string name="supp_service_additional_ect_connected" msgid="8525934162945220237">"Hoàn tất chuyển cuộc gọi ở chế độ rõ."</string>
<string name="supp_service_additional_ect_connecting" msgid="7046240728781222753">"Đang chuyển cuộc gọi ở chế độ rõ."</string>
- <string name="supp_service_call_on_hold" msgid="2836811319594503059">"Cuộc gọi đang chờ."</string>
+ <string name="supp_service_call_on_hold" msgid="2836811319594503059">"Cuộc gọi đang giữ máy."</string>
<string name="supp_service_call_resumed" msgid="3786864005920743546">"Đã tiếp tục cuộc gọi."</string>
<string name="supp_service_deflected_call" msgid="7565979024562921707">"Cuộc gọi đã bị chuyển hướng."</string>
<string name="supp_service_forwarded_call" msgid="6475776013771821457">"Cuộc gọi đã được chuyển tiếp."</string>
@@ -817,7 +817,7 @@
<string name="radio_info_data_connection_enable" msgid="6183729739783252840">"Bật kết nối dữ liệu"</string>
<string name="radio_info_data_connection_disable" msgid="6404751291511368706">"Tắt kết nối dữ liệu"</string>
<string name="volte_provisioned_switch_string" msgid="4812874990480336178">"Đã cấp phép VoLTE"</string>
- <string name="vt_provisioned_switch_string" msgid="8295542122512195979">"Đã cấp phép gọi điện video"</string>
+ <string name="vt_provisioned_switch_string" msgid="8295542122512195979">"Đã cấp phép gọi video"</string>
<string name="wfc_provisioned_switch_string" msgid="3835004640321078988">"Đã cấp phép Gọi qua Wi-Fi"</string>
<string name="eab_provisioned_switch_string" msgid="4449676720736033035">"Đã cấp phép hiện diện/EAB"</string>
<string name="cbrs_data_switch_string" msgid="6060356430838077653">"Dữ liệu Cbrs"</string>
@@ -836,7 +836,7 @@
<string name="radio_info_ims_reg_status_not_registered" msgid="8045821447288876085">"Chưa đăng ký"</string>
<string name="radio_info_ims_feature_status_available" msgid="6493200914756969292">"Có sẵn"</string>
<string name="radio_info_ims_feature_status_unavailable" msgid="8930391136839759778">"Không có sẵn"</string>
- <string name="radio_info_ims_reg_status" msgid="25582845222446390">"Đăng ký IMS: <xliff:g id="STATUS">%1$s</xliff:g>\nThoại qua LTE: <xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nThoại qua Wi-Fi: <xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\nGọi điện video: <xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nGiao diện UT: <xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
+ <string name="radio_info_ims_reg_status" msgid="25582845222446390">"Đăng ký IMS: <xliff:g id="STATUS">%1$s</xliff:g>\nThoại qua LTE: <xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nThoại qua Wi-Fi: <xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\nGọi video: <xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nGiao diện UT: <xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
<string name="radioInfo_service_in" msgid="45753418231446400">"Đang sử dụng"</string>
<string name="radioInfo_service_out" msgid="287972405416142312">"Không có dịch vụ"</string>
<string name="radioInfo_service_emergency" msgid="4763879891415016848">"Chỉ cuộc gọi khẩn cấp"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"Mã phụ của SIM dữ liệu mặc định:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Băng thông DL (kb/giây):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Băng thông UL (kb/giây):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Thông tin vị trí mạng di động (không dùng nữa):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Cấu hình kênh LTE thực:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Tốc độ làm mới thông tin mạng di động:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Toàn bộ thông tin về số đo mạng di động:"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 66eae10..978d5b5 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -163,7 +163,7 @@
<string name="vm_change_pin_error_mismatch" msgid="5364847280026257331">"旧的 PIN 码不匹配。"</string>
<string name="vm_change_pin_error_invalid" msgid="5230002671175580674">"新的 PIN 码包含无效字符。"</string>
<string name="vm_change_pin_error_system_error" msgid="9116483527909681791">"无法更改 PIN 码"</string>
- <string name="vvm_unsupported_message_format" msgid="4206402558577739713">"不受支持的语音邮件类型,请拨打 <xliff:g id="NUMBER">%s</xliff:g> 收听。"</string>
+ <string name="vvm_unsupported_message_format" msgid="4206402558577739713">"不受支持的语音信息类型,请拨打 <xliff:g id="NUMBER">%s</xliff:g> 收听。"</string>
<string name="network_settings_title" msgid="7560807107123171541">"移动网络"</string>
<string name="label_available" msgid="1316084116670821258">"可用网络"</string>
<string name="load_networks_progress" msgid="4051433047717401683">"正在搜索..."</string>
@@ -516,8 +516,8 @@
<string name="card_title_on_hold" msgid="9028319436626975207">"保持"</string>
<string name="card_title_hanging_up" msgid="814874106866647871">"正在挂断"</string>
<string name="card_title_in_call" msgid="8231896539567594265">"正在通话"</string>
- <string name="notification_voicemail_title" msgid="3932876181831601351">"新语音邮件"</string>
- <string name="notification_voicemail_title_count" msgid="2806950319222327082">"新语音邮件 (<xliff:g id="COUNT">%d</xliff:g>)"</string>
+ <string name="notification_voicemail_title" msgid="3932876181831601351">"新语音信息"</string>
+ <string name="notification_voicemail_title_count" msgid="2806950319222327082">"新语音信息 (<xliff:g id="COUNT">%d</xliff:g>)"</string>
<string name="notification_voicemail_text_format" msgid="5720947141702312537">"拨打 <xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>"</string>
<string name="notification_voicemail_no_vm_number" msgid="3423686009815186750">"语音信箱号码未知"</string>
<string name="notification_network_selection_title" msgid="255595526707809121">"无服务"</string>
@@ -861,7 +861,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"默认数据 SIM 卡的 SubId:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL 带宽 (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL 带宽 (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"移动网络位置信息(已弃用):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE 物理信道配置:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"移动网络信息刷新频率:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"所有移动网络测量信息:"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 3920bad..f3c4b96 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -863,7 +863,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"預設數據 SIM 卡的子 ID:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"下載頻寬 (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"上載頻寬 (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"發射站位置資料 (已淘汰):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE 實體渠道設定:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"發射站資料重新整理頻率:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"所有發射站量度資料:"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 9a7a0df..9d2fe4e 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -22,7 +22,7 @@
<string name="phoneIconLabel" msgid="3015941229249651419">"電話"</string>
<string name="fdnListLabel" msgid="4119121875004244097">"固定撥號清單"</string>
<string name="unknown" msgid="8279698889921830815">"不明"</string>
- <string name="private_num" msgid="4487990167889159992">"私人號碼"</string>
+ <string name="private_num" msgid="4487990167889159992">"隱藏號碼"</string>
<string name="payphone" msgid="7936735771836716941">"公用電話"</string>
<string name="onHold" msgid="6132725550015899006">"通話保留"</string>
<string name="carrier_mmi_msg_title" msgid="6050165242447507034">"「<xliff:g id="MMICARRIER">%s</xliff:g>」的訊息"</string>
@@ -96,7 +96,7 @@
<string name="sum_hide_caller_id" msgid="131100328602371933">"隱藏本機號碼"</string>
<string name="sum_show_caller_id" msgid="3571854755324664591">"撥出電話時顯示本機號碼"</string>
<string name="sum_default_caller_id" msgid="1767070797135682959">"使用預設值,在撥出電話時顯示本機號碼"</string>
- <string name="labelCW" msgid="8449327023861428622">"來電等待"</string>
+ <string name="labelCW" msgid="8449327023861428622">"來電等候"</string>
<string name="sum_cw_enabled" msgid="3977308526187139996">"通話時如有來電請通知我"</string>
<string name="sum_cw_disabled" msgid="3658094589461768637">"通話時如有來電請通知我"</string>
<string name="call_forwarding_settings" msgid="8937130467468257671">"來電轉接設定"</string>
@@ -863,7 +863,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"預設資料 SIM 卡的子 ID:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"下行頻寬 (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"上行頻寬 (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"行動網路位置資訊 (已不適用):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE 實體通道設定:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"行動網路資訊重新整理頻率:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"所有行動網路測量資訊:"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 8c0a4b8..7e20547 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -863,7 +863,6 @@
<string name="radio_info_dds" msgid="1122593144425697126">"I-SubId ye-SIM yedatha yokuzenzakalela:"</string>
<string name="radio_info_dl_kbps" msgid="2382922659525318726">"Umkhawulokudonsa we-DL (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="2102225400904799036">"Umkhawulokudonsa we-UL (kbps):"</string>
- <string name="radio_info_signal_location_label" msgid="6188435197086550049">"Ulwazi lwendawo lweseli (kwehlisiwe):"</string>
<string name="radio_info_phy_chan_config" msgid="1277949603275436081">"Ukulungiselelwa okuphathekayo kwesiteshi se-LTE:"</string>
<string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Isilinganiso sokuqalisa kabusha solwazi lweseli:"</string>
<string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Ulwazi lwesilinganiso seseli:"</string>
diff --git a/sip/res/values-ar/strings.xml b/sip/res/values-ar/strings.xml
index e971077..2c6408e 100644
--- a/sip/res/values-ar/strings.xml
+++ b/sip/res/values-ar/strings.xml
@@ -61,9 +61,9 @@
<string name="advanced_settings" msgid="2704644977548662872">"الإعدادات الاختيارية"</string>
<string name="auth_username_title" msgid="9002505242616662698">"اسم المستخدِم للمصادقة"</string>
<string name="auth_username_summary" msgid="6346313945275377230">"اسم المستخدِم المستخدَم للمصادقة"</string>
- <string name="default_preference_summary_username" msgid="8788114717555599222">"<لم يتم التعيين>"</string>
- <string name="default_preference_summary_password" msgid="3695366978153175549">"<لم يتم التعيين>"</string>
- <string name="default_preference_summary_domain_address" msgid="443247296785732364">"<لم يتم التعيين>"</string>
+ <string name="default_preference_summary_username" msgid="8788114717555599222">"<لم يتم الضبط>"</string>
+ <string name="default_preference_summary_password" msgid="3695366978153175549">"<لم يتم الضبط>"</string>
+ <string name="default_preference_summary_domain_address" msgid="443247296785732364">"<لم يتم الضبط>"</string>
<string name="display_name_summary" msgid="6749135030093260358">"<مثل اسم المستخدم>"</string>
<string name="optional_summary" msgid="620379377865437488">"<اختياري>"</string>
<string name="advanced_settings_show" msgid="2318728080037568529">"▷ المس لإظهار الكل"</string>
diff --git a/sip/res/values-pt-rPT/strings.xml b/sip/res/values-pt-rPT/strings.xml
index ae1b2ed..35aca3e 100644
--- a/sip/res/values-pt-rPT/strings.xml
+++ b/sip/res/values-pt-rPT/strings.xml
@@ -47,7 +47,7 @@
<string name="registration_status_failed_try_later" msgid="7855389184910312091">"O registo da conta não teve êxito: (<xliff:g id="REGISTRATION_ERROR_MESSAGE">%s</xliff:g>); tentaremos mais tarde"</string>
<string name="registration_status_invalid_credentials" msgid="8896714049938660777">"O registo da conta não teve êxito: palavra-passe ou nome de utilizador incorreto"</string>
<string name="registration_status_server_unreachable" msgid="3832339558868965604">"O registo da conta não teve êxito: verifique o nome do servidor."</string>
- <string name="third_party_account_summary" msgid="5918779106950859167">"Esta conta está atualmente a ser utilizada pela aplicação <xliff:g id="ACCOUNT_OWNER">%s</xliff:g>."</string>
+ <string name="third_party_account_summary" msgid="5918779106950859167">"Esta conta está atualmente a ser utilizada pela app <xliff:g id="ACCOUNT_OWNER">%s</xliff:g>."</string>
<string name="sip_edit_title" msgid="7438891546610820307">"Detalhes da conta SIP"</string>
<string name="sip_edit_new_title" msgid="8394790068979636381">"Detalhes da conta SIP"</string>
<string name="domain_address_title" msgid="8238078615181248579">"Servidor"</string>
diff --git a/sip/res/values-sq/strings.xml b/sip/res/values-sq/strings.xml
index 1b83380..5c50085 100644
--- a/sip/res/values-sq/strings.xml
+++ b/sip/res/values-sq/strings.xml
@@ -32,7 +32,7 @@
<string name="saving_account" msgid="3390358043846687266">"Po ruan llogarinë…"</string>
<string name="removing_account" msgid="1544132880414780408">"Po heq llogarinë…"</string>
<string name="sip_menu_save" msgid="4377112554203123060">"Ruaj"</string>
- <string name="sip_menu_discard" msgid="1883166691772895243">"Injoro"</string>
+ <string name="sip_menu_discard" msgid="1883166691772895243">"Hiq"</string>
<string name="alert_dialog_close" msgid="1734746505531110706">"Mbyll profilin"</string>
<string name="alert_dialog_ok" msgid="7806760618798687406">"Në rregull"</string>
<string name="close_profile" msgid="3756064641769751774">"Mbyll"</string>
diff --git a/src/com/android/phone/CallForwardEditPreference.java b/src/com/android/phone/CallForwardEditPreference.java
index 8e0b685..bf296f9 100644
--- a/src/com/android/phone/CallForwardEditPreference.java
+++ b/src/com/android/phone/CallForwardEditPreference.java
@@ -384,7 +384,9 @@
CallForwardInfo cfInfoArray[] = (CallForwardInfo[]) ar.result;
if (cfInfoArray == null || cfInfoArray.length == 0) {
Log.d(LOG_TAG, "handleGetCFResponse: cfInfoArray.length==0");
- mTcpListener.onError(CallForwardEditPreference.this, RESPONSE_ERROR);
+ if (!(ar.userObj instanceof Throwable)) {
+ mTcpListener.onError(CallForwardEditPreference.this, RESPONSE_ERROR);
+ }
} else {
for (int i = 0, length = cfInfoArray.length; i < length; i++) {
Log.d(LOG_TAG, "handleGetCFResponse, cfInfoArray[" + i + "]="
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index f5f24d3..52069b8 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -16,31 +16,42 @@
package com.android.phone;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
+import android.os.UserHandle;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.ims.DelegateRequest;
import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsUceAdapter.PublishState;
import android.telephony.ims.RegistrationManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsRcsController;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.aidl.IRcsUceControllerCallback;
import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateConnectionStateCallback;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.RcsFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;
import com.android.ims.ImsManager;
+import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.TelephonyPermissions;
+import com.android.internal.telephony.ims.ImsResolver;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.services.telephony.rcs.RcsFeatureController;
+import com.android.services.telephony.rcs.SipTransportController;
import com.android.services.telephony.rcs.TelephonyRcsService;
-import com.android.services.telephony.rcs.UserCapabilityExchangeImpl;
+import com.android.services.telephony.rcs.UceControllerManager;
import java.util.List;
@@ -55,6 +66,7 @@
private PhoneGlobals mApp;
private TelephonyRcsService mRcsService;
+ private ImsResolver mImsResolver;
/**
* Initialize the singleton ImsRcsController instance.
@@ -77,6 +89,7 @@
mApp = app;
TelephonyFrameworkInitializer
.getTelephonyServiceManager().getTelephonyImsServiceRegisterer().register(this);
+ mImsResolver = mApp.getImsResolver();
}
/**
@@ -197,40 +210,6 @@
}
}
- @Override
- public void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
- enforceReadPrivilegedPermission("registerUcePublishStateCallback");
- final long token = Binder.clearCallingIdentity();
- try {
- UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
- UserCapabilityExchangeImpl.class);
- if (uce == null) {
- throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
- "This subscription does not support UCE.");
- }
- uce.registerPublishStateCallback(c);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
- public void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
- enforceReadPrivilegedPermission("unregisterUcePublishStateCallback");
- final long token = Binder.clearCallingIdentity();
- try {
- UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
- UserCapabilityExchangeImpl.class);
- if (uce == null) {
- throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
- "This subscription does not support UCE.");
- }
- uce.unregisterUcePublishStateCallback(c);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
/**
* Query for the capability of an IMS RCS service
*
@@ -290,30 +269,94 @@
}
final long token = Binder.clearCallingIdentity();
try {
- UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
- UserCapabilityExchangeImpl.class);
- if (uce == null) {
+ UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+ UceControllerManager.class);
+ if (uceCtrlManager == null) {
throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
"This subscription does not support UCE.");
}
- uce.requestCapabilities(contactNumbers, c);
+ uceCtrlManager.requestCapabilities(contactNumbers, c);
+ } catch (ImsException e) {
+ throw new ServiceSpecificException(e.getCode(), e.getMessage());
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
- public int getUcePublishState(int subId) {
- enforceReadPrivilegedPermission("getUcePublishState");
+ public void requestNetworkAvailability(int subId, String callingPackage,
+ String callingFeatureId, Uri contactNumber, IRcsUceControllerCallback c) {
+ enforceReadPrivilegedPermission("requestNetworkAvailability");
+ if (!isUceSettingEnabled(subId, callingPackage, callingFeatureId)) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "The user has not enabled UCE for this subscription.");
+ }
final long token = Binder.clearCallingIdentity();
try {
- UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
- UserCapabilityExchangeImpl.class);
- if (uce == null) {
+ UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+ UceControllerManager.class);
+ if (uceCtrlManager == null) {
throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
"This subscription does not support UCE.");
}
- return uce.getUcePublishState();
+ uceCtrlManager.requestNetworkAvailability(contactNumber, c);
+ } catch (ImsException e) {
+ throw new ServiceSpecificException(e.getCode(), e.getMessage());
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public @PublishState int getUcePublishState(int subId) {
+ enforceReadPrivilegedPermission("getUcePublishState");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+ UceControllerManager.class);
+ if (uceCtrlManager == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "This subscription does not support UCE.");
+ }
+ return uceCtrlManager.getUcePublishState();
+ } catch (ImsException e) {
+ throw new ServiceSpecificException(e.getCode(), e.getMessage());
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
+ enforceReadPrivilegedPermission("registerUcePublishStateCallback");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+ UceControllerManager.class);
+ if (uceCtrlManager == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "This subscription does not support UCE.");
+ }
+ uceCtrlManager.registerPublishStateCallback(c);
+ } catch (ImsException e) {
+ throw new ServiceSpecificException(e.getCode(), e.getMessage());
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
+ enforceReadPrivilegedPermission("unregisterUcePublishStateCallback");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+ UceControllerManager.class);
+ if (uceCtrlManager == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "This subscription does not support UCE.");
+ }
+ uceCtrlManager.unregisterPublishStateCallback(c);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -348,6 +391,117 @@
}
}
+ @Override
+ public boolean isSipDelegateSupported(int subId) {
+ enforceReadPrivilegedPermission("isSipDelegateSupported");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ SipTransportController transport = getRcsFeatureController(subId).getFeature(
+ SipTransportController.class);
+ if (transport == null) {
+ return false;
+ }
+ return transport.isSupported(subId);
+ } catch (ImsException e) {
+ throw new ServiceSpecificException(e.getCode(), e.getMessage());
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ImsException.CODE_ERROR_UNSUPPORTED_OPERATION) {
+ return false;
+ }
+ throw e;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void createSipDelegate(int subId, DelegateRequest request, String packageName,
+ ISipDelegateConnectionStateCallback delegateState,
+ ISipDelegateMessageCallback delegateMessage) {
+ enforceModifyPermission();
+ if (!UserHandle.getUserHandleForUid(Binder.getCallingUid()).isSystem()) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "SipDelegate creation is only available to primary user.");
+ }
+ try {
+ int remoteUid = mApp.getPackageManager().getPackageUid(packageName, 0 /*flags*/);
+ if (Binder.getCallingUid() != remoteUid) {
+ throw new SecurityException("passed in packageName does not match the caller");
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new SecurityException("Passed in PackageName can not be found on device");
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ SipTransportController transport = getRcsFeatureController(subId).getFeature(
+ SipTransportController.class);
+ if (transport == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "This subscription does not support the creation of SIP delegates");
+ }
+ try {
+ transport.createSipDelegate(subId, request, packageName, delegateState,
+ delegateMessage);
+ } catch (ImsException e) {
+ throw new ServiceSpecificException(e.getCode(), e.getMessage());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void destroySipDelegate(int subId, ISipDelegate connection, int reason) {
+ enforceModifyPermission();
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ SipTransportController transport = getRcsFeatureController(subId).getFeature(
+ SipTransportController.class);
+ if (transport == null) {
+ return;
+ }
+ transport.destroySipDelegate(subId, connection, reason);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Registers for updates to the RcsFeature connection through the IImsServiceFeatureCallback
+ * callback.
+ */
+ @Override
+ public void registerRcsFeatureCallback(int slotId, IImsServiceFeatureCallback callback) {
+ enforceModifyPermission();
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (mImsResolver == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "Device does not support IMS");
+ }
+ mImsResolver.listenForFeature(slotId, ImsFeature.FEATURE_RCS, callback);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Unregister a previously registered IImsServiceFeatureCallback associated with an ImsFeature.
+ */
+ @Override
+ public void unregisterImsFeatureCallback(IImsServiceFeatureCallback callback) {
+ enforceModifyPermission();
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (mImsResolver == null) return;
+ mImsResolver.unregisterImsFeatureCallback(callback);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
/**
* Make sure either called from same process as self (phone) or IPC caller has read privilege.
*
diff --git a/src/com/android/phone/ImsUtil.java b/src/com/android/phone/ImsUtil.java
index 38936ec..0825cfb 100644
--- a/src/com/android/phone/ImsUtil.java
+++ b/src/com/android/phone/ImsUtil.java
@@ -25,6 +25,8 @@
import com.android.ims.ImsConfig;
import com.android.ims.ImsManager;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.imsphone.ImsPhone;
public class ImsUtil {
private static final String LOG_TAG = ImsUtil.class.getSimpleName();
@@ -128,6 +130,13 @@
return false;
}
+ // Do not promote WFC if in roaming and WFC roaming not allowed.
+ // WFC roaming setting is not modifiable, so its value is decided by the default value
+ // chosen by the carrier, hence it really means if the carrier supports WFC roaming.
+ if (getLastKnownRoamingState(phoneId) && !imsManager.isWfcRoamingEnabledByUser()) {
+ return false;
+ }
+
ConnectivityManager cm =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm != null) {
@@ -152,4 +161,13 @@
}
return subId;
}
+
+ private static boolean getLastKnownRoamingState(int phoneId) {
+ try {
+ ImsPhone imsPhone = (ImsPhone) (PhoneFactory.getPhone(phoneId).getImsPhone());
+ return imsPhone.getRoamingState();
+ } catch (NullPointerException | ClassCastException e) {
+ return false;
+ }
+ }
}
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index c2dece5..fe4a0ba 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -536,7 +536,11 @@
int slotId = SubscriptionManager.getSlotIndex(subId);
resId = (slotId == 0) ? R.drawable.stat_sys_phone_call_forward_sub1
: R.drawable.stat_sys_phone_call_forward_sub2;
- notificationTitle = subInfo.getDisplayName().toString();
+ if (subInfo.getDisplayName() != null) {
+ notificationTitle = subInfo.getDisplayName().toString();
+ } else {
+ notificationTitle = mContext.getString(R.string.labelCF);
+ }
} else {
notificationTitle = mContext.getString(R.string.labelCF);
}
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index bd2b2ed..509aa57 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -55,6 +55,7 @@
import android.util.Log;
import android.widget.Toast;
+import com.android.ims.ImsFeatureBinderRepository;
import com.android.internal.telephony.CallManager;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.MmiCode;
@@ -356,7 +357,8 @@
String defaultImsRcsPackage = getResources().getString(
R.string.config_ims_rcs_package);
mImsResolver = new ImsResolver(this, defaultImsMmtelPackage,
- defaultImsRcsPackage, PhoneFactory.getPhones().length);
+ defaultImsRcsPackage, PhoneFactory.getPhones().length,
+ new ImsFeatureBinderRepository());
mImsResolver.initialize();
}
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index d4a5898..a5db6d0 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -65,6 +65,7 @@
import android.telecom.TelecomManager;
import android.telephony.Annotation.ApnType;
import android.telephony.CallForwardingInfo;
+import android.telephony.CarrierBandwidth;
import android.telephony.CarrierConfigManager;
import android.telephony.CarrierRestrictionRules;
import android.telephony.CellIdentity;
@@ -104,8 +105,6 @@
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsConfigCallback;
-import android.telephony.ims.aidl.IImsMmTelFeature;
-import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.feature.ImsFeature;
@@ -132,6 +131,7 @@
import com.android.internal.telephony.DefaultPhoneNotifier;
import com.android.internal.telephony.HalVersion;
import com.android.internal.telephony.IBooleanConsumer;
+import com.android.internal.telephony.ICallForwardingInfoCallback;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.INumberVerificationCallback;
import com.android.internal.telephony.ITelephony;
@@ -172,6 +172,7 @@
import com.android.internal.telephony.uicc.UiccSlot;
import com.android.internal.telephony.util.LocaleUtils;
import com.android.internal.telephony.util.VoicemailNotificationSettingsUtil;
+import com.android.internal.util.FunctionalUtils;
import com.android.internal.util.HexDump;
import com.android.phone.settings.PickSmsSubscriptionActivity;
import com.android.phone.vvm.PhoneAccountHandleConverter;
@@ -289,6 +290,14 @@
private static final int EVENT_GET_CALL_WAITING_DONE = 88;
private static final int CMD_SET_CALL_WAITING = 89;
private static final int EVENT_SET_CALL_WAITING_DONE = 90;
+ private static final int CMD_ENABLE_NR_DUAL_CONNECTIVITY = 91;
+ private static final int EVENT_ENABLE_NR_DUAL_CONNECTIVITY_DONE = 92;
+ private static final int CMD_IS_NR_DUAL_CONNECTIVITY_ENABLED = 93;
+ private static final int EVENT_IS_NR_DUAL_CONNECTIVITY_ENABLED_DONE = 94;
+ private static final int CMD_GET_CDMA_SUBSCRIPTION_MODE = 95;
+ private static final int EVENT_GET_CDMA_SUBSCRIPTION_MODE_DONE = 96;
+ private static final int CMD_GET_SYSTEM_SELECTION_CHANNELS = 97;
+ private static final int EVENT_GET_SYSTEM_SELECTION_CHANNELS_DONE = 98;
// Parameters of select command.
private static final int SELECT_COMMAND = 0xA4;
@@ -751,6 +760,90 @@
handleNullReturnEvent(msg, "resetModemConfig");
break;
+ case CMD_IS_NR_DUAL_CONNECTIVITY_ENABLED: {
+ request = (MainThreadRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_IS_NR_DUAL_CONNECTIVITY_ENABLED_DONE,
+ request);
+ Phone phone = getPhoneFromRequest(request);
+ if (phone != null) {
+ phone.isNrDualConnectivityEnabled(onCompleted, request.workSource);
+ } else {
+ loge("isNRDualConnectivityEnabled: No phone object");
+ request.result = false;
+ notifyRequester(request);
+ }
+ break;
+ }
+
+ case EVENT_IS_NR_DUAL_CONNECTIVITY_ENABLED_DONE:
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ if (ar.exception == null && ar.result != null) {
+ request.result = ar.result;
+ } else {
+ // request.result must be set to something non-null
+ // for the calling thread to unblock
+ if (request.result != null) {
+ request.result = ar.result;
+ } else {
+ request.result = false;
+ }
+ if (ar.result == null) {
+ loge("isNRDualConnectivityEnabled: Empty response");
+ } else if (ar.exception instanceof CommandException) {
+ loge("isNRDualConnectivityEnabled: CommandException: "
+ + ar.exception);
+ } else {
+ loge("isNRDualConnectivityEnabled: Unknown exception");
+ }
+ }
+ notifyRequester(request);
+ break;
+
+ case CMD_ENABLE_NR_DUAL_CONNECTIVITY: {
+ request = (MainThreadRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_ENABLE_NR_DUAL_CONNECTIVITY_DONE, request);
+ Phone phone = getPhoneFromRequest(request);
+ if (phone != null) {
+ phone.setNrDualConnectivityState((int) request.argument, onCompleted,
+ request.workSource);
+ } else {
+ loge("enableNrDualConnectivity: No phone object");
+ request.result =
+ TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_RADIO_NOT_AVAILABLE;
+ notifyRequester(request);
+ }
+ break;
+ }
+
+ case EVENT_ENABLE_NR_DUAL_CONNECTIVITY_DONE: {
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ if (ar.exception == null) {
+ request.result =
+ TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_SUCCESS;
+ } else {
+ request.result =
+ TelephonyManager
+ .ENABLE_NR_DUAL_CONNECTIVITY_RADIO_ERROR;
+ if (ar.exception instanceof CommandException) {
+ CommandException.Error error =
+ ((CommandException) (ar.exception)).getCommandError();
+ if (error == CommandException.Error.RADIO_NOT_AVAILABLE) {
+ request.result =
+ TelephonyManager
+ .ENABLE_NR_DUAL_CONNECTIVITY_RADIO_NOT_AVAILABLE;
+ }
+ loge("enableNrDualConnectivity" + ": CommandException: "
+ + ar.exception);
+ } else {
+ loge("enableNrDualConnectivity" + ": Unknown exception");
+ }
+ }
+ notifyRequester(request);
+ break;
+ }
+
case CMD_GET_PREFERRED_NETWORK_TYPE:
request = (MainThreadRequest) msg.obj;
onCompleted = obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE_DONE, request);
@@ -831,38 +924,43 @@
getPhoneFromRequest(request).getAvailableNetworks(onCompleted);
break;
- case CMD_GET_CALL_FORWARDING:
+ case CMD_GET_CALL_FORWARDING: {
request = (MainThreadRequest) msg.obj;
onCompleted = obtainMessage(EVENT_GET_CALL_FORWARDING_DONE, request);
- int callForwardingReason = (Integer) request.argument;
- getPhoneFromRequest(request).getCallForwardingOption(
- callForwardingReason, onCompleted);
+ Pair<Integer, TelephonyManager.CallForwardingInfoCallback> args =
+ (Pair<Integer, TelephonyManager.CallForwardingInfoCallback>)
+ request.argument;
+ int callForwardingReason = args.first;
+ request.phone.getCallForwardingOption(callForwardingReason, onCompleted);
break;
-
- case EVENT_GET_CALL_FORWARDING_DONE:
+ }
+ case EVENT_GET_CALL_FORWARDING_DONE: {
ar = (AsyncResult) msg.obj;
request = (MainThreadRequest) ar.userObj;
- CallForwardingInfo callForwardingInfo = null;
+ TelephonyManager.CallForwardingInfoCallback callback =
+ ((Pair<Integer, TelephonyManager.CallForwardingInfoCallback>)
+ request.argument).second;
if (ar.exception == null && ar.result != null) {
+ CallForwardingInfo callForwardingInfo = null;
CallForwardInfo[] callForwardInfos = (CallForwardInfo[]) ar.result;
for (CallForwardInfo callForwardInfo : callForwardInfos) {
// Service Class is a bit mask per 3gpp 27.007. Search for
// any service for voice call.
if ((callForwardInfo.serviceClass
& CommandsInterface.SERVICE_CLASS_VOICE) > 0) {
- callForwardingInfo = new CallForwardingInfo(
- callForwardInfo.serviceClass, callForwardInfo.reason,
- callForwardInfo.number,
- callForwardInfo.timeSeconds);
+ callForwardingInfo = new CallForwardingInfo(true,
+ callForwardInfo.reason,
+ callForwardInfo.number,
+ callForwardInfo.timeSeconds);
break;
}
}
// Didn't find a call forward info for voice call.
if (callForwardingInfo == null) {
- callForwardingInfo = new CallForwardingInfo(
- CallForwardingInfo.STATUS_UNKNOWN_ERROR,
- 0 /* reason */, null /* number */, 0 /* timeout */);
+ callForwardingInfo = new CallForwardingInfo(false /* enabled */,
+ 0 /* reason */, null /* number */, 0 /* timeout */);
}
+ callback.onCallForwardingInfoAvailable(callForwardingInfo);
} else {
if (ar.result == null) {
loge("EVENT_GET_CALL_FORWARDING_DONE: Empty response");
@@ -870,56 +968,80 @@
if (ar.exception != null) {
loge("EVENT_GET_CALL_FORWARDING_DONE: Exception: " + ar.exception);
}
- int errorCode = CallForwardingInfo.STATUS_UNKNOWN_ERROR;
+ int errorCode = TelephonyManager
+ .CallForwardingInfoCallback.RESULT_ERROR_UNKNOWN;
if (ar.exception instanceof CommandException) {
CommandException.Error error =
((CommandException) (ar.exception)).getCommandError();
if (error == CommandException.Error.FDN_CHECK_FAILURE) {
- errorCode = CallForwardingInfo.STATUS_FDN_CHECK_FAILURE;
+ errorCode = TelephonyManager
+ .CallForwardingInfoCallback.RESULT_ERROR_FDN_CHECK_FAILURE;
} else if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) {
- errorCode = CallForwardingInfo.STATUS_NOT_SUPPORTED;
+ errorCode = TelephonyManager
+ .CallForwardingInfoCallback.RESULT_ERROR_NOT_SUPPORTED;
}
}
- callForwardingInfo = new CallForwardingInfo(
- errorCode, 0 /* reason */, null /* number */, 0 /* timeout */);
+ callback.onError(errorCode);
}
- request.result = callForwardingInfo;
- notifyRequester(request);
break;
+ }
- case CMD_SET_CALL_FORWARDING:
+ case CMD_SET_CALL_FORWARDING: {
request = (MainThreadRequest) msg.obj;
onCompleted = obtainMessage(EVENT_SET_CALL_FORWARDING_DONE, request);
+ request = (MainThreadRequest) msg.obj;
CallForwardingInfo callForwardingInfoToSet =
- (CallForwardingInfo) request.argument;
- getPhoneFromRequest(request).setCallForwardingOption(
- callForwardingInfoToSet.getStatus(),
+ ((Pair<CallForwardingInfo, Consumer<Integer>>)
+ request.argument).first;
+ request.phone.setCallForwardingOption(
+ callForwardingInfoToSet.isEnabled()
+ ? CommandsInterface.CF_ACTION_ENABLE
+ : CommandsInterface.CF_ACTION_DISABLE,
callForwardingInfoToSet.getReason(),
callForwardingInfoToSet.getNumber(),
callForwardingInfoToSet.getTimeoutSeconds(), onCompleted);
break;
+ }
- case EVENT_SET_CALL_FORWARDING_DONE:
+ case EVENT_SET_CALL_FORWARDING_DONE: {
ar = (AsyncResult) msg.obj;
request = (MainThreadRequest) ar.userObj;
- if (ar.exception == null) {
- request.result = true;
- } else {
- request.result = false;
+ Consumer<Integer> callback =
+ ((Pair<CallForwardingInfo, Consumer<Integer>>)
+ request.argument).second;
+ if (ar.exception != null) {
loge("setCallForwarding exception: " + ar.exception);
+ int errorCode = TelephonyManager.CallForwardingInfoCallback
+ .RESULT_ERROR_UNKNOWN;
+ if (ar.exception instanceof CommandException) {
+ CommandException.Error error =
+ ((CommandException) (ar.exception)).getCommandError();
+ if (error == CommandException.Error.FDN_CHECK_FAILURE) {
+ errorCode = TelephonyManager.CallForwardingInfoCallback
+ .RESULT_ERROR_FDN_CHECK_FAILURE;
+ } else if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) {
+ errorCode = TelephonyManager.CallForwardingInfoCallback
+ .RESULT_ERROR_NOT_SUPPORTED;
+ }
+ }
+ callback.accept(errorCode);
+ } else {
+ callback.accept(TelephonyManager.CallForwardingInfoCallback.RESULT_SUCCESS);
}
- notifyRequester(request);
break;
+ }
- case CMD_GET_CALL_WAITING:
+ case CMD_GET_CALL_WAITING: {
request = (MainThreadRequest) msg.obj;
onCompleted = obtainMessage(EVENT_GET_CALL_WAITING_DONE, request);
getPhoneFromRequest(request).getCallWaiting(onCompleted);
break;
+ }
- case EVENT_GET_CALL_WAITING_DONE:
+ case EVENT_GET_CALL_WAITING_DONE: {
ar = (AsyncResult) msg.obj;
request = (MainThreadRequest) ar.userObj;
+ Consumer<Integer> callback = (Consumer<Integer>) request.argument;
int callForwardingStatus = TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR;
if (ar.exception == null && ar.result != null) {
int[] callForwardResults = (int[]) ar.result;
@@ -927,12 +1049,12 @@
// Search for any service for voice call.
if (callForwardResults.length > 1
&& ((callForwardResults[1]
- & CommandsInterface.SERVICE_CLASS_VOICE) > 0)) {
+ & CommandsInterface.SERVICE_CLASS_VOICE) > 0)) {
callForwardingStatus = callForwardResults[0] == 0
- ? TelephonyManager.CALL_WAITING_STATUS_INACTIVE
- : TelephonyManager.CALL_WAITING_STATUS_ACTIVE;
+ ? TelephonyManager.CALL_WAITING_STATUS_DISABLED
+ : TelephonyManager.CALL_WAITING_STATUS_ENABLED;
} else {
- callForwardingStatus = TelephonyManager.CALL_WAITING_STATUS_INACTIVE;
+ callForwardingStatus = TelephonyManager.CALL_WAITING_STATUS_DISABLED;
}
} else {
if (ar.result == null) {
@@ -950,28 +1072,43 @@
}
}
}
- request.result = callForwardingStatus;
- notifyRequester(request);
+ callback.accept(callForwardingStatus);
break;
+ }
- case CMD_SET_CALL_WAITING:
+ case CMD_SET_CALL_WAITING: {
request = (MainThreadRequest) msg.obj;
onCompleted = obtainMessage(EVENT_SET_CALL_WAITING_DONE, request);
- boolean isEnable = (Boolean) request.argument;
- getPhoneFromRequest(request).setCallWaiting(isEnable, onCompleted);
+ boolean enable = ((Pair<Boolean, Consumer<Integer>>) request.argument).first;
+ getPhoneFromRequest(request).setCallWaiting(enable, onCompleted);
break;
+ }
- case EVENT_SET_CALL_WAITING_DONE:
+ case EVENT_SET_CALL_WAITING_DONE: {
ar = (AsyncResult) msg.obj;
request = (MainThreadRequest) ar.userObj;
- if (ar.exception == null) {
- request.result = true;
- } else {
- request.result = false;
+ boolean enable = ((Pair<Boolean, Consumer<Integer>>) request.argument).first;
+ Consumer<Integer> callback =
+ ((Pair<Boolean, Consumer<Integer>>) request.argument).second;
+ if (ar.exception != null) {
loge("setCallWaiting exception: " + ar.exception);
+ if (ar.exception instanceof CommandException) {
+ CommandException.Error error =
+ ((CommandException) (ar.exception)).getCommandError();
+ if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) {
+ callback.accept(TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED);
+ } else {
+ callback.accept(TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR);
+ }
+ } else {
+ callback.accept(TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR);
+ }
+ } else {
+ callback.accept(enable ? TelephonyManager.CALL_WAITING_STATUS_ENABLED
+ : TelephonyManager.CALL_WAITING_STATUS_DISABLED);
}
- notifyRequester(request);
break;
+ }
case EVENT_PERFORM_NETWORK_SCAN_DONE:
ar = (AsyncResult) msg.obj;
@@ -1250,11 +1387,27 @@
request.result = ar.exception == null;
notifyRequester(request);
break;
+ case CMD_GET_CDMA_SUBSCRIPTION_MODE:
+ request = (MainThreadRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_MODE_DONE, request);
+ getPhoneFromRequest(request).queryCdmaSubscriptionMode(onCompleted);
+ break;
+ case EVENT_GET_CDMA_SUBSCRIPTION_MODE_DONE:
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ if (ar.exception != null) {
+ request.result = TelephonyManager.CDMA_SUBSCRIPTION_RUIM_SIM;
+ } else {
+ request.result = ((int[]) ar.result)[0];
+ }
+ notifyRequester(request);
+ break;
case CMD_SET_CDMA_SUBSCRIPTION_MODE:
request = (MainThreadRequest) msg.obj;
onCompleted = obtainMessage(EVENT_SET_CDMA_SUBSCRIPTION_MODE_DONE, request);
int subscriptionMode = (int) request.argument;
- getPhoneFromRequest(request).setCdmaSubscription(subscriptionMode, onCompleted);
+ getPhoneFromRequest(request).setCdmaSubscriptionMode(
+ subscriptionMode, onCompleted);
break;
case EVENT_SET_CDMA_SUBSCRIPTION_MODE_DONE:
ar = (AsyncResult) msg.obj;
@@ -1304,19 +1457,20 @@
Log.w(LOG_TAG, "Discarded CellInfo due to Callback RemoteException");
}
break;
- case CMD_GET_CELL_LOCATION:
+ case CMD_GET_CELL_LOCATION: {
request = (MainThreadRequest) msg.obj;
WorkSource ws = (WorkSource) request.argument;
Phone phone = getPhoneFromRequest(request);
phone.getCellIdentity(ws, obtainMessage(EVENT_GET_CELL_LOCATION_DONE, request));
break;
- case EVENT_GET_CELL_LOCATION_DONE:
+ }
+ case EVENT_GET_CELL_LOCATION_DONE: {
ar = (AsyncResult) msg.obj;
request = (MainThreadRequest) ar.userObj;
if (ar.exception == null) {
request.result = ar.result;
} else {
- phone = getPhoneFromRequest(request);
+ Phone phone = getPhoneFromRequest(request);
request.result = (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA)
? new CellIdentityCdma() : new CellIdentityGsm();
}
@@ -1325,6 +1479,7 @@
request.notifyAll();
}
break;
+ }
case CMD_MODEM_REBOOT:
request = (MainThreadRequest) msg.obj;
onCompleted = obtainMessage(EVENT_RESET_MODEM_CONFIG_DONE, request);
@@ -1400,6 +1555,35 @@
notifyRequester(request);
break;
}
+ case CMD_GET_SYSTEM_SELECTION_CHANNELS: {
+ request = (MainThreadRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_GET_SYSTEM_SELECTION_CHANNELS_DONE, request);
+ Phone phone = getPhoneFromRequest(request);
+ if (phone != null) {
+ phone.getSystemSelectionChannels(onCompleted);
+ } else {
+ loge("getSystemSelectionChannels: No phone object");
+ request.result = new ArrayList<RadioAccessSpecifier>();
+ notifyRequester(request);
+ }
+ break;
+ }
+ case EVENT_GET_SYSTEM_SELECTION_CHANNELS_DONE:
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ if (ar.exception == null && ar.result != null) {
+ request.result = ar.result;
+ } else {
+ request.result = new IllegalArgumentException(
+ "Failed to retrieve system selection channels");
+ if (ar.result == null) {
+ loge("getSystemSelectionChannels: Empty response");
+ } else {
+ loge("getSystemSelectionChannels: Unknown exception");
+ }
+ }
+ notifyRequester(request);
+ break;
case EVENT_SET_FORBIDDEN_PLMNS_DONE:
ar = (AsyncResult) msg.obj;
request = (MainThreadRequest) ar.userObj;
@@ -2176,7 +2360,8 @@
int subId = mSubscriptionController.getDefaultDataSubId();
final Phone phone = getPhone(subId);
if (phone != null) {
- phone.getDataEnabledSettings().setUserDataEnabled(true);
+ phone.getDataEnabledSettings().setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, true);
return true;
} else {
return false;
@@ -2196,7 +2381,8 @@
int subId = mSubscriptionController.getDefaultDataSubId();
final Phone phone = getPhone(subId);
if (phone != null) {
- phone.getDataEnabledSettings().setUserDataEnabled(false);
+ phone.getDataEnabledSettings().setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false);
return true;
} else {
return false;
@@ -5009,58 +5195,35 @@
}
/**
- * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id for the MMTel
- * feature or {@link null} if the service is not available. If the feature is available, the
- * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
+ * Registers for updates to the MmTelFeature connection through the IImsServiceFeatureCallback
+ * callback.
*/
- public IImsMmTelFeature getMmTelFeatureAndListen(int slotId,
- IImsServiceFeatureCallback callback) {
+ @Override
+ public void registerMmTelFeatureCallback(int slotId, IImsServiceFeatureCallback callback) {
enforceModifyPermission();
final long identity = Binder.clearCallingIdentity();
try {
if (mImsResolver == null) {
- // may happen if the device does not support IMS.
- return null;
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "Device does not support IMS");
}
- return mImsResolver.getMmTelFeatureAndListen(slotId, callback);
+ mImsResolver.listenForFeature(slotId, ImsFeature.FEATURE_MMTEL, callback);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
-
- /**
- * Returns the {@link IImsRcsFeature} that corresponds to the given slot Id for the RCS
- * feature during emergency calling or {@link null} if the service is not available. If the
- * feature is available, the {@link IImsServiceFeatureCallback} callback is registered as a
- * listener for feature updates.
- */
- public IImsRcsFeature getRcsFeatureAndListen(int slotId, IImsServiceFeatureCallback callback) {
- enforceModifyPermission();
-
- final long identity = Binder.clearCallingIdentity();
- try {
- if (mImsResolver == null) {
- // may happen if the device does not support IMS.
- return null;
- }
- return mImsResolver.getRcsFeatureAndListen(slotId, callback);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
/**
* Unregister a previously registered IImsServiceFeatureCallback associated with an ImsFeature.
*/
- public void unregisterImsFeatureCallback(int slotId, int featureType,
- IImsServiceFeatureCallback callback) {
+ @Override
+ public void unregisterImsFeatureCallback(IImsServiceFeatureCallback callback) {
enforceModifyPermission();
final long identity = Binder.clearCallingIdentity();
try {
if (mImsResolver == null) return;
- mImsResolver.unregisterImsFeatureCallback(slotId, featureType, callback);
+ mImsResolver.unregisterImsFeatureCallback(callback);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -5141,6 +5304,36 @@
}
/**
+ * Clears any carrier ImsService overrides for the slot index specified that were previously
+ * set with {@link #setBoundImsServiceOverride(int, boolean, int[], String)}.
+ *
+ * This should only be used for testing.
+ *
+ * @param slotIndex the slot ID that the ImsService should bind for.
+ * @return true if clearing the carrier ImsService override succeeded or false if it did not.
+ */
+ @Override
+ public boolean clearCarrierImsServiceOverride(int slotIndex) {
+ int[] subIds = SubscriptionManager.getSubId(slotIndex);
+ TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+ "clearCarrierImsServiceOverride");
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
+ (subIds != null ? subIds[0] : SubscriptionManager.INVALID_SUBSCRIPTION_ID),
+ "clearCarrierImsServiceOverride");
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (mImsResolver == null) {
+ // may happen if the device does not support IMS.
+ return false;
+ }
+ return mImsResolver.clearCarrierImsServiceConfiguration(slotIndex);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
* Return the package name of the currently bound ImsService.
*
* @param slotId The slot that the ImsService is associated with.
@@ -5341,7 +5534,8 @@
* Get the call forwarding info, given the call forwarding reason.
*/
@Override
- public CallForwardingInfo getCallForwarding(int subId, int callForwardingReason) {
+ public void getCallForwarding(int subId, int callForwardingReason,
+ ICallForwardingInfoCallback callback) {
enforceReadPrivilegedPermission("getCallForwarding");
long identity = Binder.clearCallingIdentity();
try {
@@ -5349,8 +5543,39 @@
log("getCallForwarding: subId " + subId
+ " callForwardingReason" + callForwardingReason);
}
- return (CallForwardingInfo) sendRequest(
- CMD_GET_CALL_FORWARDING, callForwardingReason, subId);
+
+ Phone phone = getPhone(subId);
+ if (phone == null) {
+ try {
+ callback.onError(
+ TelephonyManager.CallForwardingInfoCallback.RESULT_ERROR_UNKNOWN);
+ } catch (RemoteException e) {
+ // ignore
+ }
+ return;
+ }
+
+ Pair<Integer, TelephonyManager.CallForwardingInfoCallback> argument = Pair.create(
+ callForwardingReason, new TelephonyManager.CallForwardingInfoCallback() {
+ @Override
+ public void onCallForwardingInfoAvailable(CallForwardingInfo info) {
+ try {
+ callback.onCallForwardingInfoAvailable(info);
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }
+
+ @Override
+ public void onError(int error) {
+ try {
+ callback.onError(error);
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }
+ });
+ sendRequestAsync(CMD_GET_CALL_FORWARDING, argument, phone, null);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -5361,7 +5586,8 @@
* reason, the number to forward, and the timeout before the forwarding is attempted.
*/
@Override
- public boolean setCallForwarding(int subId, CallForwardingInfo callForwardingInfo) {
+ public void setCallForwarding(int subId, CallForwardingInfo callForwardingInfo,
+ IIntegerConsumer callback) {
enforceModifyPermission();
long identity = Binder.clearCallingIdentity();
try {
@@ -5369,38 +5595,79 @@
log("setCallForwarding: subId " + subId
+ " callForwardingInfo" + callForwardingInfo);
}
- return (Boolean) sendRequest(CMD_SET_CALL_FORWARDING, callForwardingInfo, subId);
+
+ Phone phone = getPhone(subId);
+ if (phone == null) {
+ try {
+ callback.accept(
+ TelephonyManager.CallForwardingInfoCallback.RESULT_ERROR_UNKNOWN);
+ } catch (RemoteException e) {
+ // ignore
+ }
+ return;
+ }
+
+ Pair<CallForwardingInfo, Consumer<Integer>> arguments = Pair.create(callForwardingInfo,
+ FunctionalUtils.ignoreRemoteException(callback::accept));
+
+ sendRequestAsync(CMD_SET_CALL_FORWARDING, arguments, phone, null);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/**
- * Get the call forwarding info, given the call forwarding reason.
+ * Get the call waiting status for a subId.
*/
@Override
- public int getCallWaitingStatus(int subId) {
+ public void getCallWaitingStatus(int subId, IIntegerConsumer callback) {
enforceReadPrivilegedPermission("getCallForwarding");
long identity = Binder.clearCallingIdentity();
try {
+
+ Phone phone = getPhone(subId);
+ if (phone == null) {
+ try {
+ callback.accept(TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR);
+ } catch (RemoteException e) {
+ // ignore
+ }
+ return;
+ }
+
+ Consumer<Integer> argument = FunctionalUtils.ignoreRemoteException(callback::accept);
+
if (DBG) log("getCallWaitingStatus: subId " + subId);
- return (Integer) sendRequest(CMD_GET_CALL_WAITING, null, subId);
+ sendRequestAsync(CMD_GET_CALL_WAITING, argument, phone, null);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/**
- * Sets the voice call forwarding info including status (enable/disable), call forwarding
- * reason, the number to forward, and the timeout before the forwarding is attempted.
+ * Sets whether call waiting is enabled for a given subId.
*/
@Override
- public boolean setCallWaitingStatus(int subId, boolean isEnable) {
+ public void setCallWaitingStatus(int subId, boolean enable, IIntegerConsumer callback) {
enforceModifyPermission();
long identity = Binder.clearCallingIdentity();
try {
- if (DBG) log("setCallWaitingStatus: subId " + subId + " isEnable: " + isEnable);
- return (Boolean) sendRequest(CMD_SET_CALL_WAITING, isEnable, subId);
+ if (DBG) log("setCallWaitingStatus: subId " + subId + " enable: " + enable);
+
+ Phone phone = getPhone(subId);
+ if (phone == null) {
+ try {
+ callback.accept(TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR);
+ } catch (RemoteException e) {
+ // ignore
+ }
+ return;
+ }
+
+ Pair<Boolean, Consumer<Integer>> arguments = Pair.create(enable,
+ FunctionalUtils.ignoreRemoteException(callback::accept));
+
+ sendRequestAsync(CMD_SET_CALL_WAITING, arguments, phone, null);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -5638,6 +5905,80 @@
}
/**
+ * Enable/Disable E-UTRA-NR Dual Connectivity
+ * @param subId subscription id of the sim card
+ * @param nrDualConnectivityState expected NR dual connectivity state
+ * This can be passed following states
+ * <ol>
+ * <li>Enable NR dual connectivity {@link TelephonyManager#NR_DUAL_CONNECTIVITY_ENABLE}
+ * <li>Disable NR dual connectivity {@link TelephonyManager#NR_DUAL_CONNECTIVITY_DISABLE}
+ * <li>Disable NR dual connectivity and force secondary cell to be released
+ * {@link TelephonyManager#NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE}
+ * </ol>
+ * @return operation result.
+ */
+ @Override
+ public int setNrDualConnectivityState(int subId,
+ @TelephonyManager.NrDualConnectivityState int nrDualConnectivityState) {
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "enableNRDualConnectivity");
+ WorkSource workSource = getWorkSource(Binder.getCallingUid());
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ int result = (int) sendRequest(CMD_ENABLE_NR_DUAL_CONNECTIVITY,
+ nrDualConnectivityState, subId,
+ workSource);
+ if (DBG) log("enableNRDualConnectivity result: " + result);
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Is E-UTRA-NR Dual Connectivity enabled
+ * @return true if dual connectivity is enabled else false
+ */
+ @Override
+ public boolean isNrDualConnectivityEnabled(int subId) {
+ TelephonyPermissions
+ .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "isNRDualConnectivityEnabled");
+ WorkSource workSource = getWorkSource(Binder.getCallingUid());
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ boolean isEnabled = (boolean) sendRequest(CMD_IS_NR_DUAL_CONNECTIVITY_ENABLED,
+ null, subId, workSource);
+ if (DBG) log("isNRDualConnectivityEnabled: " + isEnabled);
+ return isEnabled;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * get carrier bandwidth per primary and secondary carrier
+ * @param subId subscription id of the sim card
+ * @return CarrierBandwidth with bandwidth of both primary and secondary carrier..
+ */
+ @Override
+ public CarrierBandwidth getCarrierBandwidth(int subId) {
+ TelephonyPermissions
+ .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "isNRDualConnectivityEnabled");
+ WorkSource workSource = getWorkSource(Binder.getCallingUid());
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ CarrierBandwidth carrierBandwidth =
+ getPhoneFromSubId(subId).getCarrierBandwidth();
+ if (DBG) log("getCarrierBandwidth: " + carrierBandwidth);
+ return carrierBandwidth;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
* Get the effective allowed network types on the device.
* This API will return an intersection of allowed network types for all reasons,
* including the configuration done through setAllowedNetworkTypes
@@ -5708,33 +6049,6 @@
}
/**
- * Set mobile data enabled
- * Used by the user through settings etc to turn on/off mobile data
- *
- * @param enable {@code true} turn turn data on, else {@code false}
- */
- @Override
- public void setUserDataEnabled(int subId, boolean enable) {
- TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
- mApp, subId, "setUserDataEnabled");
-
- final long identity = Binder.clearCallingIdentity();
- try {
- int phoneId = mSubscriptionController.getPhoneId(subId);
- if (DBG) log("setUserDataEnabled: subId=" + subId + " phoneId=" + phoneId);
- Phone phone = PhoneFactory.getPhone(phoneId);
- if (phone != null) {
- if (DBG) log("setUserDataEnabled: subId=" + subId + " enable=" + enable);
- phone.getDataEnabledSettings().setUserDataEnabled(enable);
- } else {
- loge("setUserDataEnabled: no phone found. Invalid subId=" + subId);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
* Enable or disable always reporting signal strength changes from radio.
*
* @param isEnable {@code true} for enabling; {@code false} for disabling.
@@ -5824,7 +6138,18 @@
*/
@Override
public boolean isDataEnabled(int subId) {
- enforceReadPrivilegedPermission("isDataEnabled");
+ try {
+ try {
+ mApp.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_NETWORK_STATE,
+ null);
+ } catch (Exception e) {
+ mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
+ "isDataEnabled");
+ }
+ } catch (Exception e) {
+ enforceReadPrivilegedPermission("isDataEnabled");
+ }
final long identity = Binder.clearCallingIdentity();
try {
@@ -5844,6 +6169,53 @@
}
}
+ /**
+ * Check if data is enabled for a specific reason
+ * @param subId Subscription index
+ * @param reason the reason the data enable change is taking place
+ * @return {@code true} if the overall data is enabled; {@code false} if not.
+ */
+ @Override
+ public boolean isDataEnabledForReason(int subId,
+ @TelephonyManager.DataEnabledReason int reason) {
+ try {
+ mApp.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
+ null);
+ } catch (Exception e) {
+ mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
+ "isDataEnabledForReason");
+ }
+
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ int phoneId = mSubscriptionController.getPhoneId(subId);
+ if (DBG) {
+ log("isDataEnabledForReason: subId=" + subId + " phoneId=" + phoneId
+ + " reason=" + reason);
+ }
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ if (phone != null) {
+ boolean retVal;
+ if (reason == TelephonyManager.DATA_ENABLED_REASON_USER) {
+ retVal = phone.isUserDataEnabled();
+ } else {
+ retVal = phone.getDataEnabledSettings().isDataEnabledForReason(reason);
+ }
+ if (DBG) log("isDataEnabledForReason: retVal=" + retVal);
+ return retVal;
+ } else {
+ if (DBG) {
+ loge("isDataEnabledForReason: no phone subId="
+ + subId + " retVal=false");
+ }
+ return false;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
private int getCarrierPrivilegeStatusFromCarrierConfigRules(int privilegeFromSim, int uid,
Phone phone) {
if (uid == Process.SYSTEM_UID || uid == Process.PHONE_UID) {
@@ -6048,12 +6420,10 @@
final Phone phone = getPhone(subId);
UiccCard card = phone == null ? null : phone.getUiccCard();
if (card == null) {
- loge("getIccId: No UICC");
return null;
}
String iccId = card.getIccId();
if (TextUtils.isEmpty(iccId)) {
- loge("getIccId: ICC ID is null or empty.");
return null;
}
return iccId;
@@ -6680,7 +7050,8 @@
try {
if (SubscriptionManager.isUsableSubIdValue(subId) && !mUserManager.hasUserRestriction(
UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) {
- setUserDataEnabled(subId, getDefaultDataEnabled());
+ setDataEnabledForReason(subId, TelephonyManager.DATA_ENABLED_REASON_USER,
+ getDefaultDataEnabled());
setNetworkSelectionModeAutomatic(subId);
setPreferredNetworkType(subId, getDefaultNetworkType(subId));
setDataRoamingEnabled(subId, getDefaultDataRoamingEnabled(subId));
@@ -7207,31 +7578,6 @@
}
/**
- * Action set from carrier signalling broadcast receivers to enable/disable metered apns
- * @param subId the subscription ID that this action applies to.
- * @param enabled control enable or disable metered apns.
- * {@hide}
- */
- @Override
- public void carrierActionSetMeteredApnsEnabled(int subId, boolean enabled) {
- enforceModifyPermission();
- final Phone phone = getPhone(subId);
-
- final long identity = Binder.clearCallingIdentity();
- if (phone == null) {
- loge("carrierAction: SetMeteredApnsEnabled fails with invalid subId: " + subId);
- return;
- }
- try {
- phone.carrierActionSetMeteredApnsEnabled(enabled);
- } catch (Exception e) {
- Log.e(LOG_TAG, "carrierAction: SetMeteredApnsEnabled fails. Exception ex=" + e);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
* Action set from carrier signalling broadcast receivers to enable/disable radio
* @param subId the subscription ID that this action applies to.
* @param enabled control enable or disable radio.
@@ -7332,20 +7678,36 @@
}
/**
- * Policy control of data connection. Usually used when data limit is passed.
- * @param enabled True if enabling the data, otherwise disabling.
+ * Policy control of data connection with reason {@@TelephonyManager.DataEnabledReason}
* @param subId Subscription index
- * {@hide}
+ * @param reason the reason the data enable change is taking place
+ * @param enabled True if enabling the data, otherwise disabling.
+ * @hide
*/
@Override
- public void setPolicyDataEnabled(boolean enabled, int subId) {
- enforceModifyPermission();
+ public void setDataEnabledForReason(int subId, @TelephonyManager.DataEnabledReason int reason,
+ boolean enabled) {
+ if (reason == TelephonyManager.DATA_ENABLED_REASON_USER
+ || reason == TelephonyManager.DATA_ENABLED_REASON_CARRIER) {
+ try {
+ TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+ mApp, subId, "setDataEnabledForReason");
+ } catch (SecurityException se) {
+ enforceModifyPermission();
+ }
+ } else {
+ enforceModifyPermission();
+ }
final long identity = Binder.clearCallingIdentity();
try {
Phone phone = getPhone(subId);
if (phone != null) {
- phone.getDataEnabledSettings().setPolicyDataEnabled(enabled);
+ if (reason == TelephonyManager.DATA_ENABLED_REASON_CARRIER) {
+ phone.carrierActionSetMeteredApnsEnabled(enabled);
+ } else {
+ phone.getDataEnabledSettings().setDataEnabled(reason, enabled);
+ }
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -7868,6 +8230,20 @@
}
@Override
+ public int getCdmaSubscriptionMode(int subId) {
+ TelephonyPermissions
+ .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "getCdmaSubscriptionMode");
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return (int) sendRequest(CMD_GET_CDMA_SUBSCRIPTION_MODE, null /* argument */, subId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public boolean setCdmaSubscriptionMode(int subId, int mode) {
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
mApp, subId, "setCdmaSubscriptionMode");
@@ -7917,11 +8293,9 @@
try {
for (Phone phone: PhoneFactory.getPhones()) {
if (phone.getEmergencyNumberTracker() != null
- && phone.getEmergencyNumberTracker() != null) {
- if (phone.getEmergencyNumberTracker().isEmergencyNumber(
- number, exactMatch)) {
- return true;
- }
+ && phone.getEmergencyNumberTracker()
+ .isEmergencyNumber(number, exactMatch)) {
+ return true;
}
}
return false;
@@ -8302,7 +8676,7 @@
* 1) user data is turned on, or
* 2) APN is un-metered for this subscription, or
* 3) APN type is whitelisted. E.g. MMS is whitelisted if
- * {@link TelephonyManager#setAlwaysAllowMmsData} is turned on.
+ * {@link TelephonyManager#MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED} is enabled.
*
* @return whether data is allowed for a apn type.
*
@@ -8376,6 +8750,24 @@
}
@Override
+ public List<RadioAccessSpecifier> getSystemSelectionChannels(int subId) {
+ TelephonyPermissions
+ .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "getSystemSelectionChannels");
+ WorkSource workSource = getWorkSource(Binder.getCallingUid());
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ List<RadioAccessSpecifier> specifiers =
+ (List<RadioAccessSpecifier>) sendRequest(CMD_GET_SYSTEM_SELECTION_CHANNELS,
+ null, subId, workSource);
+ if (DBG) log("getSystemSelectionChannels: " + specifiers);
+ return specifiers;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public boolean isMvnoMatched(int subId, int mvnoType, @NonNull String mvnoMatchData) {
enforceReadPrivilegedPermission("isMvnoMatched");
IccRecords iccRecords = UiccController.getInstance().getIccRecords(
@@ -8444,55 +8836,54 @@
}
@Override
- public boolean setDataAllowedDuringVoiceCall(int subId, boolean allow) {
- enforceModifyPermission();
+ public boolean isMobileDataPolicyEnabled(int subscriptionId, int policy) {
+ enforceReadPrivilegedPermission("isMobileDataPolicyEnabled");
- // Now that all security checks passes, perform the operation as ourselves.
final long identity = Binder.clearCallingIdentity();
try {
- Phone phone = getPhone(subId);
+ Phone phone = getPhone(subscriptionId);
if (phone == null) return false;
- return phone.getDataEnabledSettings().setAllowDataDuringVoiceCall(allow);
+ switch (policy) {
+ case TelephonyManager.MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL:
+ return phone.getDataEnabledSettings().isDataAllowedInVoiceCall();
+ case TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED:
+ return phone.getDataEnabledSettings().isMmsAlwaysAllowed();
+ default:
+ throw new IllegalArgumentException(policy + " is not a valid policy");
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
- public boolean isDataAllowedInVoiceCall(int subId) {
- enforceReadPrivilegedPermission("isDataAllowedInVoiceCall");
-
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- try {
- Phone phone = getPhone(subId);
- if (phone == null) return false;
-
- return phone.getDataEnabledSettings().isDataAllowedInVoiceCall();
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow) {
+ public void setMobileDataPolicyEnabledStatus(int subscriptionId, int policy,
+ boolean enabled) {
enforceModifyPermission();
- // Now that all security checks passes, perform the operation as ourselves.
final long identity = Binder.clearCallingIdentity();
try {
- Phone phone = getPhone(subId);
- if (phone == null) return false;
+ Phone phone = getPhone(subscriptionId);
+ if (phone == null) return;
- return phone.getDataEnabledSettings().setAlwaysAllowMmsData(alwaysAllow);
+ switch (policy) {
+ case TelephonyManager.MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL:
+ phone.getDataEnabledSettings().setAllowDataDuringVoiceCall(enabled);
+ break;
+ case TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED:
+ phone.getDataEnabledSettings().setAlwaysAllowMmsData(enabled);
+ break;
+ default:
+ throw new IllegalArgumentException(policy + " is not a valid policy");
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/**
- * Updates whether conference event pacakge handling is enabled.
+ * Updates whether conference event package handling is enabled.
* @param isCepEnabled {@code true} if CEP handling is enabled (default), or {@code false}
* otherwise.
*/
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index c3f2974..33d0721 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -17,7 +17,6 @@
package com.android.phone;
import android.content.Context;
-import android.os.BasicShellCommandHandler;
import android.os.Binder;
import android.os.PersistableBundle;
import android.os.Process;
@@ -34,6 +33,7 @@
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.util.TelephonyUtils;
+import com.android.modules.utils.BasicShellCommandHandler;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -64,8 +64,9 @@
private static final String DATA_ENABLE = "enable";
private static final String DATA_DISABLE = "disable";
- private static final String IMS_SET_CARRIER_SERVICE = "set-ims-service";
- private static final String IMS_GET_CARRIER_SERVICE = "get-ims-service";
+ private static final String IMS_SET_IMS_SERVICE = "set-ims-service";
+ private static final String IMS_GET_IMS_SERVICE = "get-ims-service";
+ private static final String IMS_CLEAR_SERVICE_OVERRIDE = "clear-ims-service-override";
private static final String IMS_ENABLE = "enable";
private static final String IMS_DISABLE = "disable";
// Used to disable or enable processing of conference event package data from the network.
@@ -210,6 +211,11 @@
pw.println(" -d: The ImsService defined as the device default ImsService.");
pw.println(" -f: The feature type that the query will be requested for. If none is");
pw.println(" specified, the returned package name will correspond to MMTEL.");
+ pw.println(" ims clear-ims-service-override [-s SLOT_ID]");
+ pw.println(" Clear all carrier ImsService overrides. This does not work for device ");
+ pw.println(" configuration overrides. Options are:");
+ pw.println(" -s: The SIM slot ID for the registered ImsService. If no option");
+ pw.println(" is specified, it will choose the default voice SIM slot.");
pw.println(" ims enable [-s SLOT_ID]");
pw.println(" enables IMS for the SIM slot specified, or for the default voice SIM slot");
pw.println(" if none is specified.");
@@ -295,12 +301,15 @@
}
switch (arg) {
- case IMS_SET_CARRIER_SERVICE: {
+ case IMS_SET_IMS_SERVICE: {
return handleImsSetServiceCommand();
}
- case IMS_GET_CARRIER_SERVICE: {
+ case IMS_GET_IMS_SERVICE: {
return handleImsGetServiceCommand();
}
+ case IMS_CLEAR_SERVICE_OVERRIDE: {
+ return handleImsClearCarrierServiceCommand();
+ }
case IMS_ENABLE: {
return handleEnableIms();
}
@@ -547,6 +556,42 @@
return 0;
}
+ // ims clear-ims-service-override
+ private int handleImsClearCarrierServiceCommand() {
+ PrintWriter errPw = getErrPrintWriter();
+ int slotId = getDefaultSlot();
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-s": {
+ try {
+ slotId = Integer.parseInt(getNextArgRequired());
+ } catch (NumberFormatException e) {
+ errPw.println("ims set-ims-service requires an integer as a SLOT_ID.");
+ return -1;
+ }
+ break;
+ }
+ }
+ }
+
+ try {
+ boolean result = mInterface.clearCarrierImsServiceOverride(slotId);
+ if (VDBG) {
+ Log.v(LOG_TAG, "ims clear-ims-service-override -s " + slotId
+ + ", result=" + result);
+ }
+ getOutPrintWriter().println(result);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "ims clear-ims-service-override -s " + slotId
+ + ", error" + e.getMessage());
+ errPw.println("Exception: " + e.getMessage());
+ return -1;
+ }
+ return 0;
+ }
+
// ims get-ims-service
private int handleImsGetServiceCommand() {
PrintWriter errPw = getErrPrintWriter();
diff --git a/src/com/android/phone/TimeConsumingPreferenceActivity.java b/src/com/android/phone/TimeConsumingPreferenceActivity.java
index 3b5fe21..8c5ae6d 100644
--- a/src/com/android/phone/TimeConsumingPreferenceActivity.java
+++ b/src/com/android/phone/TimeConsumingPreferenceActivity.java
@@ -187,11 +187,6 @@
@Override
public void onError(Preference preference, int error) {
if (DBG) dumpState();
- if (!preference.isEnabled()) {
- Log.i(LOG_TAG, "onError, skipped duplicated error popup");
- return;
- }
-
Log.i(LOG_TAG, "onError, preference=" + preference.getKey() + ", error=" + error);
if (mIsForeground) {
diff --git a/src/com/android/services/telephony/CdmaConnection.java b/src/com/android/services/telephony/CdmaConnection.java
index 90e7663..c7b324d 100644
--- a/src/com/android/services/telephony/CdmaConnection.java
+++ b/src/com/android/services/telephony/CdmaConnection.java
@@ -16,11 +16,12 @@
package com.android.services.telephony;
+import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.telephony.DisconnectCause;
-import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
@@ -284,8 +285,14 @@
private boolean isEmergency() {
Phone phone = getPhone();
- return phone != null && getAddress() != null && PhoneNumberUtils.isLocalEmergencyNumber(
- phone.getContext(), getAddress().getSchemeSpecificPart());
+ if (phone != null && getAddress() != null) {
+ TelephonyManager tm = (TelephonyManager) phone.getContext()
+ .getSystemService(Context.TELEPHONY_SERVICE);
+ if (tm != null) {
+ return tm.isEmergencyNumber(getAddress().getSchemeSpecificPart());
+ }
+ }
+ return false;
}
/**
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 4826ecb..c9f762b 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -16,6 +16,7 @@
package com.android.services.telephony;
+import android.annotation.NonNull;
import android.content.Context;
import android.graphics.drawable.Icon;
import android.net.Uri;
@@ -28,6 +29,7 @@
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
import android.util.Pair;
import com.android.ims.internal.ConferenceParticipant;
@@ -1269,58 +1271,79 @@
}
/**
+ * Extracts a phone number from a {@link Uri}.
+ * <p>
+ * Phone numbers can be represented either as a TEL URI or a SIP URI.
+ * For conference event packages, RFC3261 specifies how participants can be identified using a
+ * SIP URI.
+ * A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers
+ * Per RFC3261, the "user" can be a telephone number.
+ * For example: sip:1650555121;phone-context=blah.com@host.com
+ * In this case, the phone number is in the user field of the URI, and the parameters can be
+ * ignored.
+ *
+ * A SIP URI can also specify a phone number in a format similar to:
+ * sip:+1-212-555-1212@something.com;user=phone
+ * In this case, the phone number is again in user field and the parameters can be ignored.
+ * We can get the user field in these instances by splitting the string on the @, ;, or :
+ * and looking at the first found item.
+ * @param handle The URI containing a SIP or TEL formatted phone number.
+ * @return extracted phone number.
+ */
+ private static @NonNull String extractPhoneNumber(@NonNull Uri handle) {
+ // Number is always in the scheme specific part, regardless of whether this is a TEL or SIP
+ // URI.
+ String number = handle.getSchemeSpecificPart();
+ // Get anything before the @ for the SIP case.
+ String[] numberParts = number.split("[@;:]");
+
+ if (numberParts.length == 0) {
+ Log.v(LOG_TAG, "extractPhoneNumber(N) : no number in handle");
+ return "";
+ }
+ return numberParts[0];
+ }
+
+ /**
* Determines if the passed in participant handle is the same as the conference host's handle.
* Starts with a simple equality check. However, the handles from a conference event package
* will be a SIP uri, so we need to pull that apart to look for the participant's phone number.
*
- * @param hostHandles The handle(s) of the connection hosting the conference.
+ * @param hostHandles The handle(s) of the connection hosting the conference, typically obtained
+ * from P-Associated-Uri entries.
* @param handle The handle of the conference participant.
* @return {@code true} if the host's handle matches the participant's handle, {@code false}
* otherwise.
*/
- private boolean isParticipantHost(Uri[] hostHandles, Uri handle) {
+ @VisibleForTesting
+ public static boolean isParticipantHost(Uri[] hostHandles, Uri handle) {
// If there is no host handle or no participant handle, bail early.
if (hostHandles == null || hostHandles.length == 0 || handle == null) {
- Log.v(this, "isParticipantHost(N) : host or participant uri null");
+ Log.v(LOG_TAG, "isParticipantHost(N) : host or participant uri null");
return false;
}
- // Conference event package participants are identified using SIP URIs (see RFC3261).
- // A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers
- // Per RFC3261, the "user" can be a telephone number.
- // For example: sip:1650555121;phone-context=blah.com@host.com
- // In this case, the phone number is in the user field of the URI, and the parameters can be
- // ignored.
- //
- // A SIP URI can also specify a phone number in a format similar to:
- // sip:+1-212-555-1212@something.com;user=phone
- // In this case, the phone number is again in user field and the parameters can be ignored.
- // We can get the user field in these instances by splitting the string on the @, ;, or :
- // and looking at the first found item.
-
- String number = handle.getSchemeSpecificPart();
- String numberParts[] = number.split("[@;:]");
-
- if (numberParts.length == 0) {
- Log.v(this, "isParticipantHost(N) : no number in participant handle");
+ String number = extractPhoneNumber(handle);
+ // If we couldn't extract the participant's number, then we can't determine if it is the
+ // host or not.
+ if (TextUtils.isEmpty(number)) {
return false;
}
- number = numberParts[0];
for (Uri hostHandle : hostHandles) {
if (hostHandle == null) {
continue;
}
- // The host number will be a tel: uri. Per RFC3966, the part after tel: is the phone
- // number.
- String hostNumber = hostHandle.getSchemeSpecificPart();
+ // Similar to the CEP participant data, the host identity in the P-Associated-Uri could
+ // be a SIP URI or a TEL URI.
+ String hostNumber = extractPhoneNumber(hostHandle);
// Use a loose comparison of the phone numbers. This ensures that numbers that differ
// by special characters are counted as equal.
// E.g. +16505551212 would be the same as 16505551212
boolean isHost = PhoneNumberUtils.compare(hostNumber, number);
- Log.v(this, "isParticipantHost(%s) : host: %s, participant %s", (isHost ? "Y" : "N"),
+ Log.v(LOG_TAG, "isParticipantHost(%s) : host: %s, participant %s", (isHost ? "Y" : "N"),
Rlog.pii(LOG_TAG, hostNumber), Rlog.pii(LOG_TAG, number));
if (isHost) {
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 46c245a..0e7c8ed 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -428,8 +428,7 @@
.getBoolean(R.bool.config_support_video_calling_fallback));
if (slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
- extras.putString(PhoneAccount.EXTRA_SORT_ORDER,
- String.valueOf(slotId));
+ extras.putInt(PhoneAccount.EXTRA_SORT_ORDER, slotId);
}
mIsMergeCallSupported = isCarrierMergeCallSupported();
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 5a2b384..09c83c0 100755
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -96,6 +96,9 @@
private static final int MSG_CONFERENCE_MERGE_FAILED = 6;
private static final int MSG_SUPP_SERVICE_NOTIFY = 7;
+ // the threshold used to compare mAudioCodecBitrateKbps and mAudioCodecBandwidth.
+ private static final float THRESHOLD = 0.01f;
+
/**
* Mappings from {@link com.android.internal.telephony.Connection} extras keys to their
* equivalents defined in {@link android.telecom.Connection}.
@@ -1325,7 +1328,9 @@
setCallerDisplayName(name, namePresentation);
}
- if (PhoneNumberUtils.isEmergencyNumber(mOriginalConnection.getAddress())) {
+ TelephonyManager tm = (TelephonyManager) getPhone().getContext()
+ .getSystemService(Context.TELEPHONY_SERVICE);
+ if (tm.isEmergencyNumber(mOriginalConnection.getAddress())) {
mTreatAsEmergencyCall = true;
}
@@ -1388,7 +1393,9 @@
mHandler.obtainMessage(MSG_CONNECTION_EXTRAS_CHANGED, connExtras == null ? null :
new Bundle(connExtras)).sendToTarget();
- if (PhoneNumberUtils.isEmergencyNumber(mOriginalConnection.getAddress())) {
+ TelephonyManager tm = (TelephonyManager) getPhone().getContext()
+ .getSystemService(Context.TELEPHONY_SERVICE);
+ if (tm.isEmergencyNumber(mOriginalConnection.getAddress())) {
mTreatAsEmergencyCall = true;
}
// Propagate VERSTAT for IMS calls.
@@ -1492,7 +1499,8 @@
}
}
- private void refreshCodecType() {
+ private void refreshCodec() {
+ boolean changed = false;
Bundle newExtras = getExtras();
if (newExtras == null) {
newExtras = new Bundle();
@@ -1508,6 +1516,31 @@
Connection.AUDIO_CODEC_NONE);
if (newCodecType != oldCodecType) {
newExtras.putInt(Connection.EXTRA_AUDIO_CODEC, newCodecType);
+ changed = true;
+ }
+ if (isImsConnection()) {
+ float newBitrate = getOriginalConnection().getAudioCodecBitrateKbps();
+ float oldBitrate = newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS, 0.0f);
+ if (Math.abs(newBitrate - oldBitrate) > THRESHOLD) {
+ newExtras.putFloat(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS, newBitrate);
+ changed = true;
+ }
+
+ float newBandwidth = getOriginalConnection().getAudioCodecBandwidthKhz();
+ float oldBandwidth = newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ,
+ 0.0f);
+ if (Math.abs(newBandwidth - oldBandwidth) > THRESHOLD) {
+ newExtras.putFloat(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ, newBandwidth);
+ changed = true;
+ }
+ } else {
+ ArrayList<String> toRemove = new ArrayList<>();
+ toRemove.add(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS);
+ toRemove.add(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ);
+ removeTelephonyExtras(toRemove);
+ }
+
+ if (changed) {
putTelephonyExtras(newExtras);
}
}
@@ -1586,7 +1619,7 @@
}
}
- isVowifiEnabled = ImsUtil.isWfcEnabled(phone.getContext(), phone.getPhoneId());
+ isVowifiEnabled = isWfcEnabled(phone);
}
if (isCurrentVideoCall) {
@@ -2191,7 +2224,7 @@
updateAddress();
updateMultiparty();
refreshDisableAddCall();
- refreshCodecType();
+ refreshCodec();
}
/**
@@ -2722,7 +2755,7 @@
boolean isIms = phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS;
boolean isVoWifiEnabled = false;
if (isIms) {
- isVoWifiEnabled = ImsUtil.isWfcEnabled(phone.getContext(), phone.getPhoneId());
+ isVoWifiEnabled = isWfcEnabled(phone);
}
boolean isRttMergeSupported = getCarrierConfig()
.getBoolean(CarrierConfigManager.KEY_ALLOW_MERGING_RTT_CALLS_BOOL);
@@ -2772,6 +2805,12 @@
notifyConferenceSupportedChanged(isConferenceSupported);
}
}
+
+ @VisibleForTesting
+ boolean isWfcEnabled(Phone phone) {
+ return ImsUtil.isWfcEnabled(phone.getContext(), phone.getPhoneId());
+ }
+
/**
* Provides a mapping from extras keys which may be found in the
* {@link com.android.internal.telephony.Connection} to their equivalents defined in
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 953d415..322993a 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -1687,7 +1687,6 @@
EmergencyNumber emergencyNumber =
phone.getEmergencyNumberTracker().getEmergencyNumber(number);
if (emergencyNumber != null) {
- phone.notifyOutgoingEmergencyCall(emergencyNumber);
if (!getAllConnections().isEmpty()) {
if (!shouldHoldForEmergencyCall(phone)) {
// If we do not support holding ongoing calls for an outgoing
diff --git a/src/com/android/services/telephony/rcs/DelegateBinderStateManager.java b/src/com/android/services/telephony/rcs/DelegateBinderStateManager.java
new file mode 100644
index 0000000..39e9965
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/DelegateBinderStateManager.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 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 com.android.services.telephony.rcs;
+
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.aidl.ISipTransport;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+/**
+ * Defines the interface to be used to manage the state of a SipDelegate on the ImsService side.
+ */
+public interface DelegateBinderStateManager {
+
+ /**
+ * Callback interface that allows listeners to listen to changes in registration or
+ * configuration state.
+ */
+ interface StateCallback {
+ /**
+ * The SipDelegate has notified telephony that the registration state has changed.
+ */
+ void onRegistrationStateChanged(DelegateRegistrationState registrationState);
+
+ /**
+ * The SipDelegate has notified telephony that the IMS configuration has changed.
+ */
+ void onImsConfigurationChanged(SipDelegateImsConfiguration config);
+ }
+
+ /** Allow for mocks to be created for testing. */
+ @VisibleForTesting
+ interface Factory {
+ /**
+ * Create a new instance of this interface, which may change depending on the tags being
+ * denied. See {@link SipDelegateBinderConnectionStub} and
+ * {@link SipDelegateBinderConnection}
+ */
+ DelegateBinderStateManager create(int subId, ISipTransport sipTransport,
+ DelegateRequest requestedConfig, Set<FeatureTagState> transportDeniedTags,
+ Executor executor, List<StateCallback> stateCallbacks);
+ }
+
+ /**
+ * Start the process to create a SipDelegate on the ImsService.
+ * @param cb The Binder interface that the SipDelegate should use to notify new incoming SIP
+ * messages as well as acknowledge whether or not an outgoing SIP message was
+ * successfully sent.
+ * @param createdConsumer The consumer that will be notified when the creation process has
+ * completed. Contains the ISipDelegate interface to communicate with the SipDelegate
+ * and the feature tags the SipDelegate itself denied.
+ * @return true if the creation process started, false if the remote process died. If false, the
+ * consumers will not be notified.
+ */
+ boolean create(ISipDelegateMessageCallback cb,
+ BiConsumer<ISipDelegate, Set<FeatureTagState>> createdConsumer);
+
+ /**
+ * Destroy the existing SipDelegate managed by this object.
+ * <p>
+ * This instance should be cleaned up after this call.
+ * @param reason The reason for why this delegate is being destroyed.
+ * @param destroyedConsumer The consumer that will be notified when this operation completes.
+ * Contains the reason the SipDelegate reported it was destroyed.
+ */
+ void destroy(int reason, Consumer<Integer> destroyedConsumer);
+}
diff --git a/src/com/android/services/telephony/rcs/DelegateStateTracker.java b/src/com/android/services/telephony/rcs/DelegateStateTracker.java
new file mode 100644
index 0000000..1d8fa3b
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/DelegateStateTracker.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2020 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 com.android.services.telephony.rcs;
+
+import android.os.RemoteException;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateConnectionStateCallback;
+import android.telephony.ims.stub.DelegateConnectionStateCallback;
+import android.util.LocalLog;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Manages the events sent back to the remote IMS application using the AIDL backing for the
+ * {@link DelegateConnectionStateCallback} interface.
+ */
+public class DelegateStateTracker implements DelegateBinderStateManager.StateCallback {
+ private static final String LOG_TAG = "DelegateST";
+
+ private final int mSubId;
+ private final ISipDelegateConnectionStateCallback mAppStateCallback;
+ private final ISipDelegate mLocalDelegateImpl;
+
+ private final LocalLog mLocalLog = new LocalLog(SipTransportController.LOG_SIZE);
+
+ private List<FeatureTagState> mDelegateDeniedTags;
+ private DelegateRegistrationState mLastRegState;
+ private boolean mCreatedCalled = false;
+ private int mRegistrationStateOverride = -1;
+
+ public DelegateStateTracker(int subId, ISipDelegateConnectionStateCallback appStateCallback,
+ ISipDelegate localDelegateImpl) {
+ mSubId = subId;
+ mAppStateCallback = appStateCallback;
+ mLocalDelegateImpl = localDelegateImpl;
+ }
+
+ /**
+ * Notify this state tracker that a new internal SipDelegate has been connected.
+ *
+ * Registration and state updates will be send via the
+ * {@link SipDelegateBinderConnection.StateCallback} callback implemented by this class as they
+ * arrive.
+ * @param deniedTags The tags denied by the SipTransportController and ImsService creating the
+ * SipDelegate. These tags will need to be notified back to the IMS application.
+ */
+ public void sipDelegateConnected(Set<FeatureTagState> deniedTags) {
+ logi("SipDelegate connected with denied tags:" + deniedTags);
+ // From the IMS application perspective, we only call onCreated/onDestroyed once and
+ // provide the local implementation of ISipDelegate, which doesn't change, even though
+ // SipDelegates may be changing underneath.
+ if (!mCreatedCalled) {
+ mCreatedCalled = true;
+ notifySipDelegateCreated();
+ }
+ mRegistrationStateOverride = -1;
+ mDelegateDeniedTags = new ArrayList<>(deniedTags);
+ }
+
+ /**
+ * The underlying SipDelegate is changing due to a state change in the SipDelegateController.
+ *
+ * This will trigger an override of the IMS application's registration state. All feature tags
+ * in the REGISTERED state will be overridden to move to the deregistering state specified until
+ * a new SipDelegate was successfully created and {@link #sipDelegateConnected(Set)} was called
+ * or it was destroyed and {@link #sipDelegateDestroyed(int)} was called.
+ * @param deregisteringReason The new deregistering reason that all feature tags in the
+ * registered state should now report.
+ */
+ public void sipDelegateChanging(int deregisteringReason) {
+ logi("SipDelegate Changing");
+ mRegistrationStateOverride = deregisteringReason;
+ if (mLastRegState == null) {
+ logw("sipDelegateChanging: invalid state, onRegistrationStateChanged never called.");
+ mLastRegState = new DelegateRegistrationState.Builder().build();
+ }
+ onRegistrationStateChanged(mLastRegState);
+ }
+
+ /**
+ * The underlying SipDelegate has been destroyed.
+ *
+ * This should only be called when the entire {@link SipDelegateController} is going down
+ * because the application has requested that the SipDelegate be destroyed.
+ *
+ * This can also be called in error conditions where the IMS application or ImsService has
+ * crashed.
+ * @param reason The reason that will be sent to the IMS application for why the SipDelegate
+ * is being destroyed.
+ */
+ public void sipDelegateDestroyed(int reason) {
+ logi("SipDelegate destroyed:" + reason);
+ mRegistrationStateOverride = -1;
+ try {
+ mAppStateCallback.onDestroyed(reason);
+ } catch (RemoteException e) {
+ logw("sipDelegateDestroyed: IMS application is dead: " + e);
+ }
+ }
+
+ /**
+ * The underlying SipDelegate has reported that its registration state has changed.
+ * @param registrationState The RegistrationState reported by the SipDelegate to be sent to the
+ * IMS application.
+ */
+ @Override
+ public void onRegistrationStateChanged(DelegateRegistrationState registrationState) {
+ if (mRegistrationStateOverride > DelegateRegistrationState.DEREGISTERED_REASON_UNKNOWN) {
+ logi("onRegistrationStateChanged: overriding registered state to "
+ + mRegistrationStateOverride);
+ registrationState = overrideRegistrationForDelegateChange(mRegistrationStateOverride,
+ registrationState);
+ }
+ if (registrationState.equals(mLastRegState)) {
+ logi("onRegistrationStateChanged: skipping notification, state is the same.");
+ return;
+ }
+ mLastRegState = registrationState;
+ logi("onRegistrationStateChanged: sending reg state " + registrationState);
+ try {
+ mAppStateCallback.onFeatureTagStatusChanged(registrationState, mDelegateDeniedTags);
+ } catch (RemoteException e) {
+ logw("onRegistrationStateChanged: IMS application is dead: " + e);
+ }
+ }
+
+ /**
+ * THe underlying SipDelegate has reported that the IMS configuration has changed.
+ * @param config The config to be sent to the IMS application.
+ */
+ @Override
+ public void onImsConfigurationChanged(SipDelegateImsConfiguration config) {
+ logi("onImsConfigurationChanged: Sending new IMS configuration.");
+ try {
+ mAppStateCallback.onImsConfigurationChanged(config);
+ } catch (RemoteException e) {
+ logw("onImsConfigurationChanged: IMS application is dead: " + e);
+ }
+ }
+
+ /** Write state about this tracker into the PrintWriter to be included in the dumpsys */
+ public void dump(PrintWriter printWriter) {
+ printWriter.println("Last reg state: " + mLastRegState);
+ printWriter.println("Denied tags: " + mDelegateDeniedTags);
+ printWriter.println("Most recent logs: ");
+ printWriter.println();
+ mLocalLog.dump(printWriter);
+ }
+
+ private DelegateRegistrationState overrideRegistrationForDelegateChange(
+ int registerOverrideReason, DelegateRegistrationState state) {
+ Set<String> registeredFeatures = state.getRegisteredFeatureTags();
+ DelegateRegistrationState.Builder overriddenState = new DelegateRegistrationState.Builder();
+ // keep other deregistering/deregistered tags the same.
+ for (FeatureTagState dereging : state.getDeregisteringFeatureTags()) {
+ overriddenState.addDeregisteringFeatureTag(dereging.getFeatureTag(),
+ dereging.getState());
+ }
+ for (FeatureTagState dereged : state.getDeregisteredFeatureTags()) {
+ overriddenState.addDeregisteredFeatureTag(dereged.getFeatureTag(),
+ dereged.getState());
+ }
+ // Override REGISTERED only
+ for (String ft : registeredFeatures) {
+ overriddenState.addDeregisteringFeatureTag(ft, registerOverrideReason);
+ }
+ return overriddenState.build();
+ }
+
+ private void notifySipDelegateCreated() {
+ try {
+ mAppStateCallback.onCreated(mLocalDelegateImpl);
+ } catch (RemoteException e) {
+ logw("notifySipDelegateCreated: IMS application is dead: " + e);
+ }
+ }
+
+ private void logi(String log) {
+ Log.i(SipTransportController.LOG_TAG, LOG_TAG + "[" + mSubId + "] " + log);
+ mLocalLog.log("[I] " + log);
+ }
+ private void logw(String log) {
+ Log.w(SipTransportController.LOG_TAG, LOG_TAG + "[" + mSubId + "] " + log);
+ mLocalLog.log("[W] " + log);
+ }
+}
diff --git a/src/com/android/services/telephony/rcs/MessageTransportStateTracker.java b/src/com/android/services/telephony/rcs/MessageTransportStateTracker.java
new file mode 100644
index 0000000..0691ae5
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/MessageTransportStateTracker.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2020 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 com.android.services.telephony.rcs;
+
+import android.os.Binder;
+import android.os.RemoteException;
+import android.telephony.ims.DelegateMessageCallback;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.stub.SipDelegate;
+import android.util.LocalLog;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Tracks the SIP message path both from the IMS application to the SipDelegate and from the
+ * SipDelegate back to the IMS Application.
+ * <p>
+ * Responsibilities include:
+ * 1) Queue incoming and outgoing SIP messages and deliver to IMS application and SipDelegate in
+ * order. If there is an error delivering the message, notify the caller.
+ * 2) TODO Perform basic validation of outgoing messages.
+ * 3) TODO Record the status of ongoing SIP Dialogs and trigger the completion of pending
+ * consumers when they are finished or call closeDialog to clean up the SIP
+ * dialogs that did not complete within the allotted timeout time.
+ * <p>
+ * Note: This handles incoming binder calls, so all calls from other processes should be handled on
+ * the provided Executor.
+ */
+public class MessageTransportStateTracker implements DelegateBinderStateManager.StateCallback {
+ private static final String TAG = "MessageST";
+
+ /**
+ * Communicates the result of verifying whether a SIP message should be sent based on the
+ * contents of the SIP message as well as if the transport is in an available state for the
+ * intended recipient of the message.
+ */
+ private static class VerificationResult {
+ public static final VerificationResult SUCCESS = new VerificationResult();
+
+ /**
+ * If {@code true}, the requested SIP message has been verified to be sent to the remote. If
+ * {@code false}, the SIP message has failed verification and should not be sent to the
+ * result. The {@link #restrictedReason} field will contain the reason for the verification
+ * failure.
+ */
+ public final boolean isVerified;
+
+ /**
+ * The reason associated with why the SIP message was not verified and generated a
+ * {@code false} result for {@link #isVerified}.
+ */
+ public final int restrictedReason;
+
+ /**
+ * Communicates a verified result of success. Use {@link #SUCCESS} instead.
+ */
+ private VerificationResult() {
+ isVerified = true;
+ restrictedReason = SipDelegateManager.MESSAGE_FAILURE_REASON_UNKNOWN;
+ }
+
+ /**
+ * The result of verifying that the SIP Message should be sent.
+ * @param reason The reason associated with why the SIP message was not verified and
+ * generated a {@code false} result for {@link #isVerified}.
+ */
+ VerificationResult(@SipDelegateManager.MessageFailureReason int reason) {
+ isVerified = false;
+ restrictedReason = reason;
+ }
+ }
+
+ // SipDelegateConnection(IMS Application) -> SipDelegate(ImsService)
+ private final ISipDelegate.Stub mSipDelegateConnection = new ISipDelegate.Stub() {
+ /**
+ * The IMS application is acknowledging that it has successfully received and processed an
+ * incoming SIP message sent by the SipDelegate in
+ * {@link ISipDelegateMessageCallback#onMessageReceived(SipMessage)}.
+ */
+ @Override
+ public void notifyMessageReceived(String viaTransactionId) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ if (mSipDelegate == null) {
+ logw("notifyMessageReceived called when SipDelegate is not associated for "
+ + "transaction id: " + viaTransactionId);
+ return;
+ }
+ try {
+ // TODO track the SIP Dialogs created/destroyed on the associated
+ // SipDelegate.
+ mSipDelegate.notifyMessageReceived(viaTransactionId);
+ } catch (RemoteException e) {
+ logw("SipDelegate not available when notifyMessageReceived was called "
+ + "for transaction id: " + viaTransactionId);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * The IMS application is acknowledging that it received an incoming SIP message sent by the
+ * SipDelegate in {@link ISipDelegateMessageCallback#onMessageReceived(SipMessage)} but it
+ * was unable to process it.
+ */
+ @Override
+ public void notifyMessageReceiveError(String viaTransactionId, int reason) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ if (mSipDelegate == null) {
+ logw("notifyMessageReceiveError called when SipDelegate is not associated "
+ + "for transaction id: " + viaTransactionId);
+ return;
+ }
+ try {
+ // TODO track the SIP Dialogs created/destroyed on the associated
+ // SipDelegate.
+ mSipDelegate.notifyMessageReceiveError(viaTransactionId, reason);
+ } catch (RemoteException e) {
+ logw("SipDelegate not available when notifyMessageReceiveError was called "
+ + "for transaction id: " + viaTransactionId);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * The IMS application is sending an outgoing SIP message to the SipDelegate to be processed
+ * and sent over the network.
+ */
+ @Override
+ public void sendMessage(SipMessage sipMessage, long configVersion) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ VerificationResult result = verifyOutgoingMessage(sipMessage);
+ if (!result.isVerified) {
+ notifyDelegateSendError("Outgoing messages restricted", sipMessage,
+ result.restrictedReason);
+ return;
+ }
+ try {
+ // TODO track the SIP Dialogs created/destroyed on the associated
+ // SipDelegate.
+ mSipDelegate.sendMessage(sipMessage, configVersion);
+ logi("sendMessage: message sent - " + sipMessage + ", configVersion: "
+ + configVersion);
+ } catch (RemoteException e) {
+ notifyDelegateSendError("RemoteException: " + e, sipMessage,
+ SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * The SipDelegateConnection is requesting that the resources associated with an ongoing SIP
+ * dialog be released as the SIP dialog is now closed.
+ */
+ @Override
+ public void closeDialog(String callId) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ if (mSipDelegate == null) {
+ logw("closeDialog called when SipDelegate is not associated, callId: "
+ + callId);
+ return;
+ }
+ try {
+ // TODO track the SIP Dialogs created/destroyed on the associated
+ // SipDelegate.
+ mSipDelegate.closeDialog(callId);
+ } catch (RemoteException e) {
+ logw("SipDelegate not available when closeDialog was called "
+ + "for call id: " + callId);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+
+ // SipDelegate(ImsService) -> SipDelegateConnection(IMS Application)
+ private final ISipDelegateMessageCallback.Stub mDelegateConnectionMessageCallback =
+ new ISipDelegateMessageCallback.Stub() {
+ /**
+ * An Incoming SIP Message has been received by the SipDelegate and is being routed
+ * to the IMS application for processing.
+ * <p>
+ * IMS application will call {@link ISipDelegate#notifyMessageReceived(String)} to
+ * acknowledge receipt of this incoming message.
+ */
+ @Override
+ public void onMessageReceived(SipMessage message) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ VerificationResult result = verifyIncomingMessage(message);
+ if (!result.isVerified) {
+ notifyAppReceiveError("Incoming messages restricted", message,
+ result.restrictedReason);
+ return;
+ }
+ try {
+ // TODO track the SIP Dialogs created/destroyed on the associated
+ // SipDelegate.
+ mAppCallback.onMessageReceived(message);
+ logi("onMessageReceived: received " + message);
+ } catch (RemoteException e) {
+ notifyAppReceiveError("RemoteException: " + e, message,
+ SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * An outgoing SIP message sent previously by the SipDelegateConnection to the SipDelegate
+ * using {@link ISipDelegate#sendMessage(SipMessage, int)} as been successfully sent.
+ */
+ @Override
+ public void onMessageSent(String viaTransactionId) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ if (mSipDelegate == null) {
+ logw("Unexpected state, onMessageSent called when SipDelegate is not "
+ + "associated");
+ }
+ try {
+ mAppCallback.onMessageSent(viaTransactionId);
+ } catch (RemoteException e) {
+ logw("Error sending onMessageSent to SipDelegateConnection, remote not"
+ + "available for transaction ID: " + viaTransactionId);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * An outgoing SIP message sent previously by the SipDelegateConnection to the SipDelegate
+ * using {@link ISipDelegate#sendMessage(SipMessage, int)} failed to be sent.
+ */
+ @Override
+ public void onMessageSendFailure(String viaTransactionId, int reason) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ if (mSipDelegate == null) {
+ logw("Unexpected state, onMessageSendFailure called when SipDelegate is not"
+ + "associated");
+ }
+ try {
+ mAppCallback.onMessageSendFailure(viaTransactionId, reason);
+ } catch (RemoteException e) {
+ logw("Error sending onMessageSendFailure to SipDelegateConnection, remote"
+ + " not available for transaction ID: " + viaTransactionId);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+
+ private final ISipDelegateMessageCallback mAppCallback;
+ private final Executor mExecutor;
+ private final int mSubId;
+ private final LocalLog mLocalLog = new LocalLog(SipTransportController.LOG_SIZE);
+
+ private ISipDelegate mSipDelegate;
+ private Consumer<Boolean> mPendingClosedConsumer;
+ private int mDelegateClosingReason = -1;
+ private int mDelegateClosedReason = -1;
+
+ public MessageTransportStateTracker(int subId, Executor executor,
+ ISipDelegateMessageCallback appMessageCallback) {
+ mSubId = subId;
+ mAppCallback = appMessageCallback;
+ mExecutor = executor;
+ }
+
+ @Override
+ public void onRegistrationStateChanged(DelegateRegistrationState registrationState) {
+ // TODO: integrate registration changes to SipMessage verification checks.
+ }
+
+ @Override
+ public void onImsConfigurationChanged(SipDelegateImsConfiguration config) {
+ // Not needed for this Tracker
+ }
+
+ /**
+ * Open the transport and allow SIP messages to be sent/received on the delegate specified.
+ * @param delegate The delegate connection to send SIP messages to on the ImsService.
+ * @param deniedFeatureTags Feature tags that have been denied. Outgoing SIP messages relating
+ * to these tags will be denied.
+ */
+ public void openTransport(ISipDelegate delegate, Set<FeatureTagState> deniedFeatureTags) {
+ mSipDelegate = delegate;
+ mDelegateClosingReason = -1;
+ mDelegateClosedReason = -1;
+ // TODO: integrate denied tags to SipMessage verification checks.
+ }
+
+ /** Dump state about this tracker that should be included in the dumpsys */
+ public void dump(PrintWriter printWriter) {
+ mLocalLog.dump(printWriter);
+ }
+
+ /**
+ * @return SipDelegate implementation to be sent to IMS application.
+ */
+ public ISipDelegate getDelegateConnection() {
+ return mSipDelegateConnection;
+ }
+
+ /**
+ * @return MessageCallback implementation to be sent to the ImsService.
+ */
+ public ISipDelegateMessageCallback getMessageCallback() {
+ return mDelegateConnectionMessageCallback;
+ }
+
+ /**
+ * Gradually close all SIP Dialogs by:
+ * 1) denying all new outgoing SIP Dialog requests with the reason specified and
+ * 2) only allowing existing SIP Dialogs to continue.
+ * <p>
+ * This will allow traffic to continue on existing SIP Dialogs until a BYE is sent and the
+ * SIP Dialogs are closed or a timeout is hit and {@link SipDelegate#closeDialog(String)} is
+ * forcefully called on all open SIP Dialogs.
+ * <p>
+ * Any outgoing out-of-dialog traffic on this transport will be denied with the provided reason.
+ * <p>
+ * Incoming out-of-dialog traffic will continue to be set up until the SipDelegate is fully
+ * closed.
+ * @param delegateClosingReason The reason code to return to
+ * {@link DelegateMessageCallback#onMessageSendFailure(String, int)} if a new out-of-dialog SIP
+ * message is received while waiting for existing Dialogs.
+ * @param closedReason reason to return to new outgoing SIP messages via
+ * {@link SipDelegate#notifyMessageReceiveError(String, int)} once the transport
+ * transitions to the fully closed state.
+ * @param resultConsumer The consumer called when the message transport has been closed. It will
+ * return {@code true} if the procedure completed successfully or {@link false} if the
+ * transport needed to be closed forcefully due to the application not responding before
+ * a timeout occurred.
+ */
+ public void closeGracefully(int delegateClosingReason, int closedReason,
+ Consumer<Boolean> resultConsumer) {
+ mDelegateClosingReason = delegateClosingReason;
+ mPendingClosedConsumer = resultConsumer;
+ mExecutor.execute(() -> {
+ // TODO: Track SIP Dialogs and complete when there are no SIP dialogs open anymore or
+ // the timeout occurs.
+ mPendingClosedConsumer.accept(true);
+ mPendingClosedConsumer = null;
+ closeTransport(closedReason);
+ });
+ }
+
+ /**
+ * Close all ongoing SIP Dialogs immediately and respond to any incoming/outgoing messages with
+ * the provided reason.
+ * @param closedReason The failure reason to provide to incoming/outgoing SIP messages
+ * if an attempt is made to send/receive a message after this method is called.
+ */
+ public void close(int closedReason) {
+ closeTransport(closedReason);
+ }
+
+ // Clean up all state related to the existing SipDelegate immediately.
+ private void closeTransport(int closedReason) {
+ // TODO: add logic to forcefully close open SIP dialogs once they are being tracked.
+ mSipDelegate = null;
+ if (mPendingClosedConsumer != null) {
+ mExecutor.execute(() -> {
+ logw("closeTransport: transport close forced with pending consumer.");
+ mPendingClosedConsumer.accept(false /*closedGracefully*/);
+ mPendingClosedConsumer = null;
+ });
+ }
+ mDelegateClosingReason = -1;
+ mDelegateClosedReason = closedReason;
+ }
+
+ private VerificationResult verifyOutgoingMessage(SipMessage message) {
+ if (mDelegateClosingReason > -1) {
+ return new VerificationResult(mDelegateClosingReason);
+ }
+ if (mDelegateClosedReason > -1) {
+ return new VerificationResult(mDelegateClosedReason);
+ }
+ if (mSipDelegate == null) {
+ logw("sendMessage called when SipDelegate is not associated." + message);
+ return new VerificationResult(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
+ }
+ return VerificationResult.SUCCESS;
+ }
+
+ private VerificationResult verifyIncomingMessage(SipMessage message) {
+ // Do not restrict incoming based on closing reason.
+ if (mDelegateClosedReason > -1) {
+ return new VerificationResult(mDelegateClosedReason);
+ }
+ return VerificationResult.SUCCESS;
+ }
+
+ private void notifyDelegateSendError(String logReason, SipMessage message, int reasonCode) {
+ // TODO parse SipMessage header for viaTransactionId.
+ logw("Error sending SipMessage[id: " + null + ", code: " + reasonCode + "] -> SipDelegate "
+ + "for reason: " + logReason);
+ try {
+ mAppCallback.onMessageSendFailure(null, reasonCode);
+ } catch (RemoteException e) {
+ logw("notifyDelegateSendError, SipDelegate is not available: " + e);
+ }
+ }
+
+ private void notifyAppReceiveError(String logReason, SipMessage message, int reasonCode) {
+ // TODO parse SipMessage header for viaTransactionId.
+ logw("Error sending SipMessage[id: " + null + ", code: " + reasonCode + "] -> "
+ + "SipDelegateConnection for reason: " + logReason);
+ try {
+ mSipDelegate.notifyMessageReceiveError(null, reasonCode);
+ } catch (RemoteException e) {
+ logw("notifyAppReceiveError, SipDelegate is not available: " + e);
+ }
+ }
+
+ private void logi(String log) {
+ Log.w(SipTransportController.LOG_TAG, TAG + "[" + mSubId + "] " + log);
+ mLocalLog.log("[I] " + log);
+ }
+
+ private void logw(String log) {
+ Log.w(SipTransportController.LOG_TAG, TAG + "[" + mSubId + "] " + log);
+ mLocalLog.log("[W] " + log);
+ }
+}
diff --git a/src/com/android/services/telephony/rcs/RcsFeatureController.java b/src/com/android/services/telephony/rcs/RcsFeatureController.java
index fcfe312..304a74d 100644
--- a/src/com/android/services/telephony/rcs/RcsFeatureController.java
+++ b/src/com/android/services/telephony/rcs/RcsFeatureController.java
@@ -20,7 +20,9 @@
import android.content.Context;
import android.net.Uri;
import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsRcsManager;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.RegistrationManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -28,7 +30,7 @@
import android.util.Log;
import com.android.ims.FeatureConnector;
-import com.android.ims.IFeatureConnector;
+import com.android.ims.FeatureUpdates;
import com.android.ims.RcsFeatureManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.imsphone.ImsRegistrationCallbackHelper;
@@ -72,19 +74,25 @@
* Called when the feature should be destroyed.
*/
void onDestroy();
+
+ /**
+ * Called when a dumpsys is being generated for this RcsFeatureController for all Features
+ * to report their status.
+ */
+ void dump(PrintWriter pw);
}
/**
* Used to inject FeatureConnector instances for testing.
*/
@VisibleForTesting
- public interface FeatureConnectorFactory<T extends IFeatureConnector> {
+ public interface FeatureConnectorFactory<U extends FeatureUpdates> {
/**
- * @return a {@link FeatureConnector} associated for the given {@link IFeatureConnector}
- * and slot id.
+ * @return a {@link FeatureConnector} associated for the given {@link FeatureUpdates}
+ * and slot index.
*/
- FeatureConnector<T> create(Context context, int slotId,
- FeatureConnector.Listener<T> listener, Executor executor, String tag);
+ FeatureConnector<U> create(Context context, int slotIndex,
+ FeatureConnector.Listener<U> listener, Executor executor, String logPrefix);
}
/**
@@ -100,7 +108,8 @@
ImsRegistrationCallbackHelper.ImsRegistrationUpdate cb, Executor executor);
}
- private FeatureConnectorFactory<RcsFeatureManager> mFeatureFactory = FeatureConnector::new;
+ private FeatureConnectorFactory<RcsFeatureManager> mFeatureFactory =
+ RcsFeatureManager::getConnector;
private RegistrationHelperFactory mRegistrationHelperFactory =
ImsRegistrationCallbackHelper::new;
@@ -115,11 +124,6 @@
private FeatureConnector.Listener<RcsFeatureManager> mFeatureConnectorListener =
new FeatureConnector.Listener<RcsFeatureManager>() {
@Override
- public RcsFeatureManager getFeatureManager() {
- return new RcsFeatureManager(mContext, mSlotId);
- }
-
- @Override
public void connectionReady(RcsFeatureManager manager)
throws com.android.ims.ImsException {
if (manager == null) {
@@ -140,7 +144,10 @@
}
@Override
- public void connectionUnavailable() {
+ public void connectionUnavailable(int reason) {
+ if (reason == FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE) {
+ loge("unexpected - connectionUnavailable due to server unavailable");
+ }
// Call before disabling connection to manager.
removeConnectionToService();
updateConnectionStatus(null /*manager*/);
@@ -279,7 +286,7 @@
}
@VisibleForTesting
- public void setFeatureConnectorFactory(FeatureConnectorFactory factory) {
+ public void setFeatureConnectorFactory(FeatureConnectorFactory<RcsFeatureManager> factory) {
mFeatureFactory = factory;
}
@@ -426,6 +433,14 @@
pw.print("connected=");
synchronized (mLock) {
pw.println(mFeatureManager != null);
+ pw.println();
+ pw.println("RcsFeatureControllers:");
+ pw.increaseIndent();
+ for (Feature f : mFeatures.values()) {
+ f.dump(pw);
+ pw.println();
+ }
+ pw.decreaseIndent();
}
}
@@ -433,6 +448,10 @@
Log.w(LOG_TAG, getLogPrefix().append(log).toString());
}
+ private void loge(String log) {
+ Log.e(LOG_TAG, getLogPrefix().append(log).toString());
+ }
+
private StringBuilder getLogPrefix() {
StringBuilder sb = new StringBuilder("[");
sb.append(mSlotId);
diff --git a/src/com/android/services/telephony/rcs/SipDelegateBinderConnection.java b/src/com/android/services/telephony/rcs/SipDelegateBinderConnection.java
new file mode 100644
index 0000000..1a77f2b
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/SipDelegateBinderConnection.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2020 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 com.android.services.telephony.rcs;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.aidl.ISipDelegateStateCallback;
+import android.telephony.ims.aidl.ISipTransport;
+import android.telephony.ims.stub.SipDelegate;
+import android.util.LocalLog;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+/**
+ * Container for the active connection to the {@link SipDelegate} active on the ImsService.
+ * <p>
+ * New instances of this class will be created and destroyed new {@link SipDelegate}s are created
+ * and destroyed by the {@link SipDelegateController}.
+ */
+public class SipDelegateBinderConnection implements DelegateBinderStateManager,
+ IBinder.DeathRecipient {
+ private static final String LOG_TAG = "BinderConn";
+
+ protected final int mSubId;
+ protected final Set<FeatureTagState> mDeniedTags;
+ protected final Executor mExecutor;
+ protected final List<StateCallback> mStateCallbacks;
+
+ private final LocalLog mLocalLog = new LocalLog(SipTransportController.LOG_SIZE);
+
+ // Callback interface from ImsService to this Connection. State Events will be forwarded to IMS
+ // application through DelegateStateTracker.
+ private final ISipDelegateStateCallback mSipDelegateStateCallback =
+ new ISipDelegateStateCallback.Stub() {
+ @Override
+ public void onCreated(ISipDelegate delegate,
+ List<FeatureTagState> deniedFeatureTags) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ notifySipDelegateCreated(delegate, deniedFeatureTags));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void onFeatureTagRegistrationChanged(
+ DelegateRegistrationState registrationState) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ logi("onFeatureTagRegistrationChanged:" + registrationState);
+ for (StateCallback c : mStateCallbacks) {
+ c.onRegistrationStateChanged(registrationState);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void onImsConfigurationChanged(
+ SipDelegateImsConfiguration registeredSipConfig) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ logi("onImsConfigurationChanged");
+ for (StateCallback c : mStateCallbacks) {
+ c.onImsConfigurationChanged(registeredSipConfig);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void onDestroyed(int reason) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> notifySipDelegateDestroyed(reason));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+
+ private final ISipTransport mSipTransport;
+ private final DelegateRequest mRequestedConfig;
+
+ private ISipDelegate mDelegateBinder;
+ private BiConsumer<ISipDelegate, Set<FeatureTagState>> mPendingCreatedConsumer;
+ private Consumer<Integer> mPendingDestroyedConsumer;
+
+ /**
+ * Create a new Connection object to manage the creation and destruction of a
+ * {@link SipDelegate}.
+ * @param subId The subid that this SipDelegate is being created for.
+ * @param sipTransport The SipTransport implementation that will be used to manage SipDelegates.
+ * @param requestedConfig The DelegateRequest to be sent to the ImsService.
+ * @param transportDeniedTags The feature tags that have already been denied by the
+ * SipTransportController and should not be requested.
+ * @param executor The Executor that all binder calls from the remote process will be executed
+ * on.
+ * @param stateCallbacks A list of callbacks that will each be called when the state of the
+ * SipDelegate changes. This will be called on the supplied executor.
+ */
+ public SipDelegateBinderConnection(int subId, ISipTransport sipTransport,
+ DelegateRequest requestedConfig, Set<FeatureTagState> transportDeniedTags,
+ Executor executor, List<StateCallback> stateCallbacks) {
+ mSubId = subId;
+ mSipTransport = sipTransport;
+ mRequestedConfig = requestedConfig;
+ mDeniedTags = transportDeniedTags;
+ mExecutor = executor;
+ mStateCallbacks = stateCallbacks;
+ }
+
+ @Override
+ public boolean create(ISipDelegateMessageCallback cb,
+ BiConsumer<ISipDelegate, Set<FeatureTagState>> createdConsumer) {
+ try {
+ mSipTransport.createSipDelegate(mSubId, mRequestedConfig, mSipDelegateStateCallback,
+ cb);
+ mSipTransport.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ logw("create called on unreachable SipTransport:" + e);
+ return false;
+ }
+ mPendingCreatedConsumer = createdConsumer;
+ return true;
+ }
+
+ @Override
+ public void destroy(int reason, Consumer<Integer> destroyedConsumer) {
+ mPendingDestroyedConsumer = destroyedConsumer;
+ try {
+ if (mDelegateBinder != null) {
+ mSipTransport.destroySipDelegate(mDelegateBinder, reason);
+ } else {
+ mExecutor.execute(() -> notifySipDelegateDestroyed(reason));
+ }
+ mStateCallbacks.clear();
+ } catch (RemoteException e) {
+ logw("destroy called on unreachable SipTransport:" + e);
+ mExecutor.execute(() -> notifySipDelegateDestroyed(reason));
+ }
+ try {
+ mSipTransport.asBinder().unlinkToDeath(this, 0);
+ } catch (NoSuchElementException e) {
+ logw("unlinkToDeath called on already unlinked binder" + e);
+ }
+ }
+
+ private void notifySipDelegateCreated(ISipDelegate delegate,
+ List<FeatureTagState> deniedFeatureTags) {
+ logi("Delegate Created: " + delegate + ", deniedTags:" + deniedFeatureTags);
+ if (delegate == null) {
+ logw("Invalid null delegate returned!");
+ }
+ mDelegateBinder = delegate;
+ // Add denied feature tags from SipDelegate to the ones denied by the transport
+ if (deniedFeatureTags != null) {
+ mDeniedTags.addAll(deniedFeatureTags);
+ }
+ if (mPendingCreatedConsumer == null) return;
+ mPendingCreatedConsumer.accept(delegate, mDeniedTags);
+ mPendingCreatedConsumer = null;
+ }
+
+ private void notifySipDelegateDestroyed(int reason) {
+ logi("Delegate Destroyed, reason: " + reason);
+ mDelegateBinder = null;
+ if (mPendingDestroyedConsumer == null) return;
+ mPendingDestroyedConsumer.accept(reason);
+ mPendingDestroyedConsumer = null;
+ }
+
+ /** Dump state about this binder connection that should be included in the dumpsys. */
+ public void dump(PrintWriter printWriter) {
+ mLocalLog.dump(printWriter);
+ }
+
+ protected final void logi(String log) {
+ Log.i(SipTransportController.LOG_TAG, LOG_TAG + "[" + mSubId + "] " + log);
+ mLocalLog.log("[I] " + log);
+ }
+
+ protected final void logw(String log) {
+ Log.w(SipTransportController.LOG_TAG, LOG_TAG + "[" + mSubId + "] " + log);
+ mLocalLog.log("[W] " + log);
+ }
+
+ @Override
+ public void binderDied() {
+ mExecutor.execute(() -> {
+ logw("binderDied!");
+ // Unblock any pending create/destroy operations.
+ // SipTransportController will handle the overall destruction/teardown.
+ notifySipDelegateCreated(null, Collections.emptyList());
+ notifySipDelegateDestroyed(SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+ });
+ }
+}
diff --git a/src/com/android/services/telephony/rcs/SipDelegateBinderConnectionStub.java b/src/com/android/services/telephony/rcs/SipDelegateBinderConnectionStub.java
new file mode 100644
index 0000000..888af94
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/SipDelegateBinderConnectionStub.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 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 com.android.services.telephony.rcs;
+
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.stub.SipDelegate;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+/**
+ * Stub implementation used when a SipDelegate needs to be set up in specific cases, but there
+ * is no underlying implementation in the ImsService.
+ *
+ * This is used in cases where all of the requested feature tags were denied for various reasons
+ * from the SipTransportController. In this case, we will "connect", send a update to include the
+ * denied feature tags, and then do nothing until this stub is torn down.
+ */
+public class SipDelegateBinderConnectionStub implements DelegateBinderStateManager {
+ protected final Set<FeatureTagState> mDeniedTags;
+ protected final Executor mExecutor;
+ protected final List<StateCallback> mStateCallbacks;
+
+ /**
+ * Create a new Connection object to manage the creation and destruction of a
+ * {@link SipDelegate}.
+ * @param transportDeniedTags The feature tags that have already been denied by the
+ * SipTransportController and should not be requested.
+ * @param executor The Executor that all binder calls from the remote process will be executed
+ * on.
+ * @param stateCallbacks A list of callbacks that will each be called when the state of the
+ * SipDelegate changes. This will be called on the supplied executor.
+ */
+ public SipDelegateBinderConnectionStub(Set<FeatureTagState> transportDeniedTags,
+ Executor executor, List<StateCallback> stateCallbacks) {
+ mDeniedTags = transportDeniedTags;
+ mExecutor = executor;
+ mStateCallbacks = stateCallbacks;
+ }
+
+ @Override
+ public boolean create(ISipDelegateMessageCallback cb,
+ BiConsumer<ISipDelegate, Set<FeatureTagState>> createdConsumer) {
+ mExecutor.execute(() -> {
+ createdConsumer.accept(null, (mDeniedTags));
+ for (SipDelegateBinderConnection.StateCallback c: mStateCallbacks) {
+ c.onRegistrationStateChanged(new DelegateRegistrationState.Builder().build());
+ }
+ });
+ return true;
+ }
+
+ @Override
+ public void destroy(int reason, Consumer<Integer> destroyedConsumer) {
+ mExecutor.execute(() -> {
+ mStateCallbacks.clear();
+ destroyedConsumer.accept(reason);
+ });
+ }
+}
diff --git a/src/com/android/services/telephony/rcs/SipDelegateController.java b/src/com/android/services/telephony/rcs/SipDelegateController.java
new file mode 100644
index 0000000..ed50778
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/SipDelegateController.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2020 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 com.android.services.telephony.rcs;
+
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConnection;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateConnectionStateCallback;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.aidl.ISipTransport;
+import android.telephony.ims.stub.DelegateConnectionStateCallback;
+import android.telephony.ims.stub.SipDelegate;
+import android.util.LocalLog;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ScheduledExecutorService;
+
+/**
+ * Created when an IMS application wishes to open up a {@link SipDelegateConnection} and manages the
+ * resulting {@link SipDelegate} that may be created on the ImsService side.
+ */
+public class SipDelegateController {
+ static final String LOG_TAG = "SipDelegateC";
+
+ private DelegateBinderStateManager.Factory mBinderConnectionFactory =
+ new DelegateBinderStateManager.Factory() {
+ @Override
+ public DelegateBinderStateManager create(int subId, ISipTransport sipTransport,
+ DelegateRequest requestedConfig, Set<FeatureTagState> transportDeniedTags,
+ Executor executor, List<DelegateBinderStateManager.StateCallback> stateCallbacks) {
+ // We should not actually create a SipDelegate in this case.
+ if (requestedConfig.getFeatureTags().isEmpty()) {
+ return new SipDelegateBinderConnectionStub(transportDeniedTags, executor,
+ stateCallbacks);
+ }
+ return new SipDelegateBinderConnection(mSubId, mSipTransportImpl, requestedConfig,
+ transportDeniedTags, mExecutorService, stateCallbacks);
+ }
+ };
+
+ private final int mSubId;
+ private final String mPackageName;
+ private final DelegateRequest mInitialRequest;
+ private final ISipTransport mSipTransportImpl;
+ private final ScheduledExecutorService mExecutorService;
+ private final MessageTransportStateTracker mMessageTransportStateTracker;
+ private final DelegateStateTracker mDelegateStateTracker;
+ private final LocalLog mLocalLog = new LocalLog(SipTransportController.LOG_SIZE);
+
+ private DelegateBinderStateManager mBinderConnection;
+ private Set<String> mTrackedFeatureTags;
+
+ public SipDelegateController(int subId, DelegateRequest initialRequest, String packageName,
+ ISipTransport sipTransportImpl, ScheduledExecutorService executorService,
+ ISipDelegateConnectionStateCallback stateCallback,
+ ISipDelegateMessageCallback messageCallback) {
+ mSubId = subId;
+ mPackageName = packageName;
+ mInitialRequest = initialRequest;
+ mSipTransportImpl = sipTransportImpl;
+ mExecutorService = executorService;
+
+ mMessageTransportStateTracker = new MessageTransportStateTracker(mSubId, executorService,
+ messageCallback);
+ mDelegateStateTracker = new DelegateStateTracker(mSubId, stateCallback,
+ mMessageTransportStateTracker.getDelegateConnection());
+ }
+
+ /**
+ * Inject dependencies for testing only.
+ */
+ @VisibleForTesting
+ public SipDelegateController(int subId, DelegateRequest initialRequest, String packageName,
+ ISipTransport sipTransportImpl, ScheduledExecutorService executorService,
+ MessageTransportStateTracker messageTransportStateTracker,
+ DelegateStateTracker delegateStateTracker,
+ DelegateBinderStateManager.Factory connectionFactory) {
+ mSubId = subId;
+ mInitialRequest = initialRequest;
+ mPackageName = packageName;
+ mSipTransportImpl = sipTransportImpl;
+ mExecutorService = executorService;
+ mMessageTransportStateTracker = messageTransportStateTracker;
+ mDelegateStateTracker = delegateStateTracker;
+ mBinderConnectionFactory = connectionFactory;
+ }
+
+ /**
+ * @return The InitialRequest from the IMS application. The feature tags that are actually set
+ * up may differ from this request based on the state of this controller.
+ */
+ public DelegateRequest getInitialRequest() {
+ return mInitialRequest;
+ }
+
+ /**
+ * @return The package name of the IMS application associated with this SipDelegateController.
+ */
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public ISipDelegate getSipDelegateInterface() {
+ return mMessageTransportStateTracker.getDelegateConnection();
+ }
+
+ /**
+ * Create the underlying SipDelegate.
+ * <p>
+ * This may not happen instantly, The CompletableFuture returned will not complete until
+ * {@link DelegateConnectionStateCallback#onCreated(SipDelegateConnection)} is called by the
+ * SipDelegate or DelegateStateTracker state is updated in the case that all requested features
+ * were denied.
+ * @return A CompletableFuture that will complete once the SipDelegate has been created. If true
+ * is returned, the SipDelegate has been created successfully. If false, the ImsService is not
+ * reachable and the process should be aborted.
+ */
+ public CompletableFuture<Boolean> create(Set<String> supportedSet,
+ Set<FeatureTagState> deniedSet) {
+ logi("create, supported: " + supportedSet + ", denied: " + deniedSet);
+ mTrackedFeatureTags = supportedSet;
+ DelegateBinderStateManager connection =
+ createBinderConnection(supportedSet, deniedSet);
+ CompletableFuture<Pair<ISipDelegate, Set<FeatureTagState>>> pendingCreate =
+ createSipDelegate(connection);
+ // May need to implement special case handling where SipDelegate denies all in supportedSet,
+ // however that should be a very rare case. For now, if that happens, just keep the
+ // SipDelegate bound.
+ // use thenApply here because we need this to happen on the same thread that it was called
+ // on in order to ensure ordering of onCreated being called, followed by registration
+ // state changed. If not, this is subject to race conditions where registered is queued
+ // before the async processing of this future.
+ return pendingCreate.thenApply((resultPair) -> {
+ if (resultPair == null) {
+ logw("create: resultPair returned null");
+ return false;
+ }
+ mBinderConnection = connection;
+ logi("create: created, delegate denied: " + resultPair.second);
+ mMessageTransportStateTracker.openTransport(resultPair.first, resultPair.second);
+ mDelegateStateTracker.sipDelegateConnected(resultPair.second);
+ return true;
+ });
+ }
+
+ /**
+ * Modify the SipTransport to reflect the new Feature Tag set that the IMS application has
+ * access to.
+ * <p>
+ * This involves the following operations if the new supported tag set does not match the
+ * the existing set:
+ * 1) destroy the existing underlying SipDelegate. If there are SIP Dialogs that are active
+ * on the SipDelegate that is pending to be destroyed, we must move the feature tags into a
+ * deregistering state via
+ * {@link DelegateRegistrationState#DEREGISTERING_REASON_FEATURE_TAGS_CHANGING} to signal to the
+ * IMS application to close all dialogs before the operation can proceed. If any outgoing
+ * out-of-dialog messages are sent at this time, they will also fail with reason
+ * {@link SipDelegateManager#MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION}.
+ * 2) create a new underlying SipDelegate and notify trackers, allowing the transport to
+ * re-open.
+ * @param newSupportedSet The new supported set of feature tags that the SipDelegate should
+ * be opened for.
+ * @param deniedSet The new set of tags that have been denied as well as the reason for the
+ * denial to be reported back to the IMS Application.
+ * @return A CompletableFuture containing the pending operation that will change the supported
+ * feature tags. Any operations to change the supported feature tags of the associated
+ * SipDelegate after this should not happen until this pending operation completes. Will
+ * complete with {@code true} if the operation was successful or {@code false} if the
+ * IMS service was unreachable.
+ */
+ public CompletableFuture<Boolean> changeSupportedFeatureTags(Set<String> newSupportedSet,
+ Set<FeatureTagState> deniedSet) {
+ logi("Received feature tag set change, old: [" + mTrackedFeatureTags + "], new: "
+ + newSupportedSet + ",denied: [" + deniedSet + "]");
+ if (mTrackedFeatureTags != null && mTrackedFeatureTags.equals(newSupportedSet)) {
+ logi("changeSupportedFeatureTags: no change, returning");
+ return CompletableFuture.completedFuture(true);
+ }
+
+ mTrackedFeatureTags = newSupportedSet;
+ // Next perform the destroy operation.
+ CompletableFuture<Integer> pendingDestroy = destroySipDelegate(false/*force*/,
+ SipDelegateManager.MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION,
+ SipDelegateManager.MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION,
+ DelegateRegistrationState.DEREGISTERING_REASON_FEATURE_TAGS_CHANGING,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+
+ // Next perform the create operation with the new set of supported feature tags.
+ return pendingDestroy.thenComposeAsync((reasonFromService) -> {
+ logi("changeSupportedFeatureTags: destroy stage complete, reason reported: "
+ + reasonFromService);
+ return create(newSupportedSet, deniedSet);
+ }, mExecutorService);
+ }
+
+ /**
+ * Destroy this SipDelegate. This controller should be disposed of after this method is
+ * called.
+ * <p>
+ * This may not happen instantly if there are SIP Dialogs that are active on this SipDelegate.
+ * In this case, the CompletableFuture will not complete until
+ * {@link DelegateConnectionStateCallback#onDestroyed(int)} is called by the SipDelegate.
+ * @param force If set true, we will close the transport immediately and call
+ * {@link SipDelegate#closeDialog(String)} on any open dialogs. If false, we will wait for the
+ * SIP Dialogs to close or the close timer to timeout before destroying the underlying
+ * SipDelegate.
+ * @param destroyReason The reason for why this SipDelegate is being destroyed.
+ * @return A CompletableFuture that will complete once the SipDelegate has been destroyed.
+ */
+ public CompletableFuture<Integer> destroy(boolean force, int destroyReason) {
+ logi("destroy, forced " + force + ", destroyReason: " + destroyReason);
+
+ CompletableFuture<Integer> pendingOperationComplete =
+ destroySipDelegate(force, SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
+ getMessageFailReasonFromDestroyReason(destroyReason),
+ DelegateRegistrationState.DEREGISTERING_REASON_DESTROY_PENDING,
+ destroyReason);
+ return pendingOperationComplete.thenApplyAsync((reasonFromDelegate) -> {
+ logi("destroy, operation complete, notifying trackers, reason" + reasonFromDelegate);
+ mDelegateStateTracker.sipDelegateDestroyed(reasonFromDelegate);
+ return reasonFromDelegate;
+ }, mExecutorService);
+ };
+
+ private static int getMessageFailReasonFromDestroyReason(int destroyReason) {
+ switch (destroyReason) {
+ case SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD:
+ return SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD;
+ case SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP:
+ case SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS:
+ return SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED;
+ default:
+ return SipDelegateManager.MESSAGE_FAILURE_REASON_UNKNOWN;
+ }
+ }
+
+ /**
+ * @param force If set true, we will close the transport immediately and call
+ * {@link SipDelegate#closeDialog(String)} on any open dialogs. If false, we will wait for the
+ * SIP Dialogs to close or the close timer to timeout before destroying the underlying
+ * SipDelegate.
+ * @param messageDestroyingReason The reason to send back to the IMS application in the case
+ * that a new outgoing SIP message is sent that is out-of-dialog while the message
+ * transport is closing.
+ * @param messageDestroyedReason The reason to send back to the IMS application in the case
+ * that a new outgoing SIP message is sent once the underlying transport is closed.
+ * @param deregisteringReason The deregistering state reported to the IMS application for all
+ * registered feature tags.
+ * @param delegateDestroyedReason The reason to send to the underlying SipDelegate that is being
+ * destroyed.
+ * @return A CompletableFuture containing the reason from the SipDelegate for why it was
+ * destroyed.
+ */
+ private CompletableFuture<Integer> destroySipDelegate(boolean force,
+ int messageDestroyingReason, int messageDestroyedReason, int deregisteringReason,
+ int delegateDestroyedReason) {
+ if (mBinderConnection == null) {
+ logi("destroySipDelegate, called when binder connection is already null");
+ return CompletableFuture.completedFuture(delegateDestroyedReason);
+ }
+ // First, bring down the message transport.
+ CompletableFuture<Boolean> pendingTransportClosed = new CompletableFuture<>();
+ if (force) {
+ logi("destroySipDelegate, forced");
+ mMessageTransportStateTracker.close(messageDestroyedReason);
+ pendingTransportClosed.complete(true);
+ } else {
+ mMessageTransportStateTracker.closeGracefully(messageDestroyingReason,
+ messageDestroyedReason, pendingTransportClosed::complete);
+ }
+
+ // Do not send an intermediate pending state to app if there are no open SIP dialogs to
+ // worry about.
+ if (!pendingTransportClosed.isDone()) {
+ mDelegateStateTracker.sipDelegateChanging(deregisteringReason);
+ } else {
+ logi("destroySipDelegate, skip DEREGISTERING_REASON_DESTROY_PENDING");
+ }
+
+ // Next, destroy the SipDelegate.
+ return pendingTransportClosed.thenComposeAsync((wasGraceful) -> {
+ logi("destroySipDelegate, transport gracefully closed = " + wasGraceful);
+ CompletableFuture<Integer> pendingDestroy = new CompletableFuture<>();
+ mBinderConnection.destroy(delegateDestroyedReason, pendingDestroy::complete);
+ return pendingDestroy;
+ }, mExecutorService);
+ }
+
+ /**
+ * @return a CompletableFuture that returns a Pair containing SipDelegate Binder interface as
+ * well as rejected feature tags or a {@code null} Pair instance if the ImsService is not
+ * available.
+ */
+ private CompletableFuture<Pair<ISipDelegate, Set<FeatureTagState>>> createSipDelegate(
+ DelegateBinderStateManager connection) {
+ CompletableFuture<Pair<ISipDelegate, Set<FeatureTagState>>> createdFuture =
+ new CompletableFuture<>();
+ boolean isStarted = connection.create(mMessageTransportStateTracker.getMessageCallback(),
+ (delegate, delegateDeniedTags) ->
+ createdFuture.complete(new Pair<>(delegate, delegateDeniedTags)));
+ if (!isStarted) {
+ logw("Couldn't create binder connection, ImsService is not available.");
+ connection.destroy(SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD, null);
+ return CompletableFuture.completedFuture(null);
+ }
+ return createdFuture;
+ }
+
+ private DelegateBinderStateManager createBinderConnection(Set<String> supportedSet,
+ Set<FeatureTagState> deniedSet) {
+
+ List<DelegateBinderStateManager.StateCallback> stateCallbacks = new ArrayList<>(2);
+ stateCallbacks.add(mDelegateStateTracker);
+ stateCallbacks.add(mMessageTransportStateTracker);
+
+ return mBinderConnectionFactory.create(mSubId, mSipTransportImpl,
+ new DelegateRequest(supportedSet), deniedSet, mExecutorService, stateCallbacks);
+ }
+
+ /**
+ * Write the current state of this controller in String format using the PrintWriter provided
+ * for dumpsys.
+ */
+ public void dump(PrintWriter printWriter) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ pw.println("SipDelegateController" + "[" + mSubId + "]:");
+ pw.increaseIndent();
+ pw.println("DelegateStateTracker:");
+ pw.increaseIndent();
+ mDelegateStateTracker.dump(printWriter);
+ pw.decreaseIndent();
+ pw.println("MessageStateTracker:");
+ pw.increaseIndent();
+ mMessageTransportStateTracker.dump(printWriter);
+ pw.decreaseIndent();
+ pw.decreaseIndent();
+ }
+
+ private void logi(String log) {
+ Log.w(SipTransportController.LOG_TAG, LOG_TAG + "[" + mSubId + "] " + log);
+ mLocalLog.log("[I] " + log);
+ }
+
+ private void logw(String log) {
+ Log.w(SipTransportController.LOG_TAG, LOG_TAG + "[" + mSubId + "] " + log);
+ mLocalLog.log("[W] " + log);
+ }
+}
diff --git a/src/com/android/services/telephony/rcs/SipTransportController.java b/src/com/android/services/telephony/rcs/SipTransportController.java
new file mode 100644
index 0000000..5d817ba
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/SipTransportController.java
@@ -0,0 +1,889 @@
+/*
+ * Copyright (C) 2020 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 com.android.services.telephony.rcs;
+
+import android.app.role.OnRoleHoldersChangedListener;
+import android.app.role.RoleManager;
+import android.content.Context;
+import android.os.UserHandle;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateConnectionStateCallback;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.aidl.ISipTransport;
+import android.telephony.ims.stub.DelegateConnectionMessageCallback;
+import android.telephony.ims.stub.DelegateConnectionStateCallback;
+import android.telephony.ims.stub.SipDelegate;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.LocalLog;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.ims.RcsFeatureManager;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+
+import com.google.common.base.Objects;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+/**
+ * Manages the creation and destruction of SipDelegates in response to an IMS application requesting
+ * a SipDelegateConnection registered to one or more IMS feature tags.
+ * <p>
+ * This allows an IMS application to forward traffic related to those feature tags over the existing
+ * IMS registration managed by the {@link ImsService} associated with this cellular subscription
+ * instead of requiring that the IMS application manage its own IMS registration over-the-top. This
+ * is required for some cellular carriers, which mandate that all IMS SIP traffic must be sent
+ * through a single IMS registration managed by the system IMS service.
+ *
+ * //TODO: Support other roles besides SMS
+ * //TODO: Bring in carrier provisioning to influence features that can be created.
+ * //TODO: Generate registration change events.
+ */
+public class SipTransportController implements RcsFeatureController.Feature,
+ OnRoleHoldersChangedListener {
+ static final int LOG_SIZE = 50;
+ static final String LOG_TAG = "SipTransportC";
+
+ /**See {@link TimerAdapter#getReevaluateThrottleTimerMilliseconds()}.*/
+ private static final int REEVALUATE_THROTTLE_DEFAULT_MS = 1000;
+ /**See {@link TimerAdapter#getUpdateRegistrationDelayMilliseconds()}.*/
+ private static final int TRIGGER_UPDATE_REGISTRATION_DELAY_DEFAULT_MS = 1000;
+
+ /**
+ * {@link RoleManager} is final so we have to wrap the implementation for testing.
+ */
+ @VisibleForTesting
+ public interface RoleManagerAdapter {
+ /** See {@link RoleManager#getRoleHolders(String)} */
+ List<String> getRoleHolders(String roleName);
+ /** See {@link RoleManager#addOnRoleHoldersChangedListenerAsUser} */
+ void addOnRoleHoldersChangedListenerAsUser(Executor executor,
+ OnRoleHoldersChangedListener listener, UserHandle user);
+ /** See {@link RoleManager#removeOnRoleHoldersChangedListenerAsUser} */
+ void removeOnRoleHoldersChangedListenerAsUser(OnRoleHoldersChangedListener listener,
+ UserHandle user);
+ }
+
+ /**
+ * Adapter for timers related to this class so they can be modified during testing.
+ */
+ @VisibleForTesting
+ public interface TimerAdapter {
+ /**
+ * Time we will delay after a {@link #createSipDelegate} or {@link #destroySipDelegate}
+ * command to re-evaluate and apply any changes to the list of tracked
+ * SipDelegateControllers.
+ * <p>
+ * Another create/destroy request sent during this time will not postpone re-evaluation
+ * again.
+ */
+ int getReevaluateThrottleTimerMilliseconds();
+
+ /**
+ * Time after re-evaluate we will wait to trigger the update of IMS registration.
+ * <p>
+ * Another re-evaluate while waiting to trigger a registration update will cause this
+ * controller to cancel and reschedule the event again, further delaying the trigger to send
+ * a registration update.
+ */
+ int getUpdateRegistrationDelayMilliseconds();
+ }
+
+ private static class TimerAdapterImpl implements TimerAdapter {
+
+ @Override
+ public int getReevaluateThrottleTimerMilliseconds() {
+ return REEVALUATE_THROTTLE_DEFAULT_MS;
+ }
+
+ @Override
+ public int getUpdateRegistrationDelayMilliseconds() {
+ return TRIGGER_UPDATE_REGISTRATION_DELAY_DEFAULT_MS;
+ }
+ }
+
+ private static class RoleManagerAdapterImpl implements RoleManagerAdapter {
+
+ private final RoleManager mRoleManager;
+
+ private RoleManagerAdapterImpl(Context context) {
+ mRoleManager = context.getSystemService(RoleManager.class);
+ }
+
+ @Override
+ public List<String> getRoleHolders(String roleName) {
+ return mRoleManager.getRoleHolders(roleName);
+ }
+
+ @Override
+ public void addOnRoleHoldersChangedListenerAsUser(Executor executor,
+ OnRoleHoldersChangedListener listener, UserHandle user) {
+ mRoleManager.addOnRoleHoldersChangedListenerAsUser(executor, listener, user);
+ }
+
+ @Override
+ public void removeOnRoleHoldersChangedListenerAsUser(OnRoleHoldersChangedListener listener,
+ UserHandle user) {
+ mRoleManager.removeOnRoleHoldersChangedListenerAsUser(listener, user);
+ }
+ }
+
+ /**
+ * Used in {@link #destroySipDelegate(int, ISipDelegate, int)} to store pending destroy
+ * requests.
+ */
+ private static final class DestroyRequest {
+ public final SipDelegateController controller;
+ public final int reason;
+
+ DestroyRequest(SipDelegateController c, int r) {
+ controller = c;
+ reason = r;
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DestroyRequest that = (DestroyRequest) o;
+ return reason == that.reason
+ && controller.equals(that.controller);
+ }
+
+ @Override
+ public int hashCode() {
+ return java.util.Objects.hash(controller, reason);
+ }
+
+ @Override
+ public String toString() {
+ return "DestroyRequest{" + "controller=" + controller + ", reason=" + reason + '}';
+ }
+ }
+
+ /**
+ * Allow the ability for tests to easily mock out the SipDelegateController for testing.
+ */
+ @VisibleForTesting
+ public interface SipDelegateControllerFactory {
+ /** See {@link SipDelegateController} */
+ SipDelegateController create(int subId, DelegateRequest initialRequest, String packageName,
+ ISipTransport sipTransportImpl, ScheduledExecutorService executorService,
+ ISipDelegateConnectionStateCallback stateCallback,
+ ISipDelegateMessageCallback messageCallback);
+ }
+
+ private SipDelegateControllerFactory mDelegateControllerFactory = SipDelegateController::new;
+ private final int mSlotId;
+ private final ScheduledExecutorService mExecutorService;
+ private final RoleManagerAdapter mRoleManagerAdapter;
+ private final TimerAdapter mTimerAdapter;
+ private final LocalLog mLocalLog = new LocalLog(LOG_SIZE);
+
+ // A priority queue of active SipDelegateControllers, where the oldest SipDelegate gets
+ // access to the feature tag if multiple apps are allowed to request the same feature tag.
+ private final List<SipDelegateController> mDelegatePriorityQueue = new ArrayList<>();
+ // SipDelegateControllers who have been created and are pending to be added to the priority
+ // queue. Will be added into the queue in the same order as they were added here.
+ private final List<SipDelegateController> mDelegatePendingCreate = new ArrayList<>();
+ // SipDelegateControllers that are pending to be destroyed.
+ private final List<DestroyRequest> mDelegatePendingDestroy = new ArrayList<>();
+
+ // Future scheduled for operations that require the list of SipDelegateControllers to
+ // be evaluated. When the timer expires and triggers the reevaluate method, this controller
+ // will iterate through mDelegatePriorityQueue and assign Feature Tags based on role+priority.
+ private ScheduledFuture<?> mScheduledEvaluateFuture;
+ // mPendingEvaluateFTFuture creates this CompletableFuture, exposed in order to stop other
+ // evaluates from occurring while another is waiting for a result on other threads.
+ private CompletableFuture<Void> mEvaluateCompleteFuture;
+ // Future scheduled that will trigger the ImsService to update the IMS registration for the
+ // SipDelegate configuration. Will be scheduled TRIGGER_UPDATE_REGISTRATION_DELAY_MS
+ // milliseconds after a pending evaluate completes.
+ private ScheduledFuture<?> mPendingUpdateRegistrationFuture;
+ // Subscription id will change as new subscriptions are loaded on the slot.
+ private int mSubId;
+ // Will go up/down as the ImsService associated with this slotId goes up/down.
+ private RcsFeatureManager mRcsManager;
+ // Cached package name of the app that is considered the default SMS app.
+ private String mCachedSmsRolePackageName = "";
+
+ /**
+ * Create an instance of SipTransportController.
+ * @param context The Context associated with this controller.
+ * @param slotId The slot index associated with this controller.
+ * @param subId The subscription ID associated with this controller when it was first created.
+ */
+ public SipTransportController(Context context, int slotId, int subId) {
+ mSlotId = slotId;
+ mSubId = subId;
+
+ mRoleManagerAdapter = new RoleManagerAdapterImpl(context);
+ mTimerAdapter = new TimerAdapterImpl();
+ mExecutorService = Executors.newSingleThreadScheduledExecutor();
+ }
+
+ /**
+ * Constructor to inject dependencies for testing.
+ */
+ @VisibleForTesting
+ public SipTransportController(Context context, int slotId, int subId,
+ SipDelegateControllerFactory delegateFactory, RoleManagerAdapter roleManagerAdapter,
+ TimerAdapter timerAdapter, ScheduledExecutorService executor) {
+ mSlotId = slotId;
+ mSubId = subId;
+
+ mRoleManagerAdapter = roleManagerAdapter;
+ mTimerAdapter = timerAdapter;
+ mDelegateControllerFactory = delegateFactory;
+ mExecutorService = executor;
+ logi("created");
+ }
+
+ @Override
+ public void onRcsConnected(RcsFeatureManager manager) {
+ mExecutorService.submit(() -> onRcsManagerChanged(manager));
+ }
+
+ @Override
+ public void onRcsDisconnected() {
+ mExecutorService.submit(() -> onRcsManagerChanged(null));
+ }
+
+ @Override
+ public void onAssociatedSubscriptionUpdated(int subId) {
+ mExecutorService.submit(()-> onSubIdChanged(subId));
+ }
+
+ @Override
+ public void onDestroy() {
+ mExecutorService.submit(()-> {
+ // Ensure new create/destroy requests are denied.
+ mSubId = -1;
+ triggerDeregistrationEvent();
+ scheduleDestroyDelegates(
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN)
+ .thenRun(mExecutorService::shutdown);
+ });
+ }
+
+ /**
+ * Optionally create a new {@link SipDelegate} based off of the {@link DelegateRequest} given
+ * based on the state of this controller and associate it with the given callbacks.
+ * <p>
+ * Once the {@link SipDelegate} has been created,
+ * {@link ISipDelegateConnectionStateCallback#onCreated(ISipDelegate)} must be called with
+ * the AIDL instance corresponding to the remote {@link SipDelegate}.
+ * @param subId the subId associated with the request.
+ * @param request The request parameters used to create the {@link SipDelegate}.
+ * @param delegateState The {@link DelegateConnectionStateCallback} Binder connection.
+ * @param delegateMessage The {@link DelegateConnectionMessageCallback} Binder Connection
+ * @throws ImsException if the request to create the {@link SipDelegate} did not complete.
+ */
+ public void createSipDelegate(int subId, DelegateRequest request, String packageName,
+ ISipDelegateConnectionStateCallback delegateState,
+ ISipDelegateMessageCallback delegateMessage) throws ImsException {
+ logi("createSipDelegate: request= " + request + ", packageName= " + packageName);
+ CompletableFuture<ImsException> result = new CompletableFuture<>();
+ mExecutorService.submit(() -> createSipDelegateInternal(subId, request, packageName,
+ delegateState,
+ // Capture any ImsExceptions generated during the process.
+ delegateMessage, result::complete));
+ try {
+ ImsException e = result.get();
+ logi("createSipDelegate: request finished");
+ if (e != null) {
+ throw e;
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ logw("createSipDelegate: exception completing future: " + e);
+ }
+ }
+
+ /**
+ * The remote IMS application has requested the destruction of an existing {@link SipDelegate}.
+ * @param subId The subId associated with the request.
+ * @param connection The internal Binder connection associated with the {@link SipDelegate}.
+ * @param reason The reason for why the {@link SipDelegate} was destroyed.
+ */
+ public void destroySipDelegate(int subId, ISipDelegate connection, int reason) {
+ mExecutorService.execute(() -> destroySipDelegateInternal(subId, connection, reason));
+ }
+
+ /**
+ * @return Whether or not SipTransports are supported on the connected ImsService. This can
+ * change based on the capabilities of the ImsService.
+ * @throws ImsException if the ImsService connected to this controller is currently down.
+ */
+ public boolean isSupported(int subId) throws ImsException {
+ Boolean result = waitForMethodToComplete(() -> isSupportedInternal(subId));
+ if (result == null) {
+ logw("isSupported, unexpected null result, returning false");
+ return false;
+ }
+ return result;
+ }
+
+ private void createSipDelegateInternal(int subId, DelegateRequest request, String packageName,
+ ISipDelegateConnectionStateCallback delegateState,
+ ISipDelegateMessageCallback delegateMessage,
+ Consumer<ImsException> startedErrorConsumer) {
+ ISipTransport transport;
+ // Send back any errors via Consumer early in creation process if it is clear that the
+ // SipDelegate will never be created.
+ try {
+ checkStateOfController(subId);
+ transport = mRcsManager.getSipTransport();
+ if (transport == null) {
+ logw("createSipDelegateInternal, transport null during request.");
+ startedErrorConsumer.accept(new ImsException("SipTransport not supported",
+ ImsException.CODE_ERROR_UNSUPPORTED_OPERATION));
+ return;
+ } else {
+ // Release the binder thread as there were no issues processing the initial request.
+ startedErrorConsumer.accept(null);
+ }
+ } catch (ImsException e) {
+ logw("createSipDelegateInternal, ImsException during create: " + e);
+ startedErrorConsumer.accept(e);
+ return;
+ }
+
+ SipDelegateController c = mDelegateControllerFactory.create(subId, request, packageName,
+ transport, mExecutorService, delegateState, delegateMessage);
+ logi("createSipDelegateInternal: request= " + request + ", packageName= " + packageName
+ + ", controller created: " + c);
+ addPendingCreateAndEvaluate(c);
+ }
+
+ private void destroySipDelegateInternal(int subId, ISipDelegate connection, int reason) {
+ if (subId != mSubId) {
+ logw("destroySipDelegateInternal: ignoring destroy, as this is about to be destroyed "
+ + "anyway due to subId change, delegate=" + connection);
+ return;
+ }
+ if (connection == null) {
+ logw("destroySipDelegateInternal: ignoring destroy, null connection binder.");
+ return;
+ }
+ SipDelegateController match = null;
+ for (SipDelegateController controller : mDelegatePriorityQueue) {
+ if (Objects.equal(connection.asBinder(),
+ controller.getSipDelegateInterface().asBinder())) {
+ match = controller;
+ break;
+ }
+ }
+ if (match == null) {
+ logw("destroySipDelegateInternal: could not find matching connection=" + connection);
+ return;
+ }
+
+ logi("destroySipDelegateInternal: destroy queued for connection= " + connection);
+ addPendingDestroyAndEvaluate(match, reason);
+ }
+
+ /**
+ * Cancel pending update IMS registration events if they exist and instead send a deregister
+ * event.
+ */
+ private void triggerDeregistrationEvent() {
+ if (mPendingUpdateRegistrationFuture != null
+ && !mPendingUpdateRegistrationFuture.isDone()) {
+ // Cancel pending update and replace with a call to deregister now.
+ mPendingUpdateRegistrationFuture.cancel(false);
+ logi("triggerDeregistrationEvent: cancelling existing reg update event: "
+ + mPendingUpdateRegistrationFuture);
+ }
+ logi("triggerDeregistrationEvent: Sending deregister event to ImsService");
+ //TODO hook up registration apis
+ }
+
+ /**
+ * Schedule an update to the IMS registration. If there is an existing update scheduled, cancel
+ * it and reschedule.
+ */
+ private void scheduleUpdateRegistration() {
+ if (mPendingUpdateRegistrationFuture != null
+ && !mPendingUpdateRegistrationFuture.isDone()) {
+ // Cancel the old pending operation and reschedule again.
+ mPendingUpdateRegistrationFuture.cancel(false);
+ logi("scheduleUpdateRegistration: cancelling existing reg update event: "
+ + mPendingUpdateRegistrationFuture);
+ }
+ ScheduledFuture<?> f = mExecutorService.schedule(this::triggerUpdateRegistrationEvent,
+ mTimerAdapter.getUpdateRegistrationDelayMilliseconds(), TimeUnit.MILLISECONDS);
+ logi("scheduleUpdateRegistration: scheduling new event: " + f);
+ mPendingUpdateRegistrationFuture = f;
+ }
+
+ private void triggerUpdateRegistrationEvent() {
+ logi("triggerUpdateRegistrationEvent: Sending update registration event to ImsService");
+ //TODO hook up registration apis
+ }
+
+ /**
+ * Returns whether or not the ImsService implementation associated with the supplied subId
+ * supports the SipTransport APIs.
+ * <p>
+ * This should only be called on the ExecutorService.
+ * @return true if SipTransport is supported on this subscription, false otherwise.
+ * @throws ImsException thrown if there was an error determining the state of the ImsService.
+ */
+ private boolean isSupportedInternal(int subId) throws ImsException {
+ checkStateOfController(subId);
+ return (mRcsManager.getSipTransport() != null);
+ }
+
+ private boolean addPendingDestroy(SipDelegateController c, int reason) {
+ DestroyRequest request = new DestroyRequest(c, reason);
+ if (!mDelegatePendingDestroy.contains(request)) {
+ return mDelegatePendingDestroy.add(request);
+ }
+ return false;
+ }
+
+ /**
+ * The supplied SipDelegateController has been destroyed and associated feature tags have been
+ * released. Trigger the re-evaluation of the priority queue with the new configuration.
+ */
+ private void addPendingDestroyAndEvaluate(SipDelegateController c, int reason) {
+ if (addPendingDestroy(c, reason)) {
+ scheduleThrottledReevaluate();
+ }
+ }
+
+ /**
+ * A new SipDelegateController has been created, add to the back of the priority queue and
+ * trigger the re-evaluation of the priority queue with the new configuration.
+ */
+ private void addPendingCreateAndEvaluate(SipDelegateController c) {
+ mDelegatePendingCreate.add(c);
+ scheduleThrottledReevaluate();
+ }
+
+ /**
+ * The priority queue has changed, which will cause a re-evaluation of the feature tags granted
+ * to each SipDelegate.
+ * <p>
+ * Note: re-evaluations are throttled to happen at a minimum to occur every
+ * REEVALUATE_THROTTLE_MS seconds. We also do not reevaluate while another reevaluate operation
+ * is in progress, so in this case, defer schedule itself.
+ */
+ private void scheduleThrottledReevaluate() {
+ if (isEvaluatePendingAndNotInProgress()) {
+ logi("scheduleThrottledReevaluate: throttling reevaluate, eval already pending: "
+ + mScheduledEvaluateFuture);
+ } else {
+ mScheduledEvaluateFuture = mExecutorService.schedule(this::reevaluateDelegates,
+ mTimerAdapter.getReevaluateThrottleTimerMilliseconds(), TimeUnit.MILLISECONDS);
+ logi("scheduleThrottledReevaluate: new reevaluate scheduled: "
+ + mScheduledEvaluateFuture);
+ }
+ }
+
+ /**
+ * @return true if there is a evaluate pending, false if there is not. If evaluate has already
+ * begun, but we are waiting for it to complete, this will also return false.
+ */
+ private boolean isEvaluatePendingAndNotInProgress() {
+ boolean isEvalScheduled = mScheduledEvaluateFuture != null
+ && !mScheduledEvaluateFuture.isDone();
+ boolean isEvalInProgress = mEvaluateCompleteFuture != null
+ && !mEvaluateCompleteFuture.isDone();
+ return isEvalScheduled && !isEvalInProgress;
+ }
+
+ /**
+ * Cancel any pending re-evaluates and perform it as soon as possible. This is done in the case
+ * where we need to do something like tear down this controller or change subId.
+ */
+ private void scheduleReevaluateNow(CompletableFuture<Void> onDoneFuture) {
+ if (isEvaluatePendingAndNotInProgress()) {
+ mScheduledEvaluateFuture.cancel(false /*interrupt*/);
+ logi("scheduleReevaluateNow: cancelling pending reevaluate: "
+ + mScheduledEvaluateFuture);
+ }
+ // we have tasks that depend on this potentially, so once the last reevaluate is done,
+ // schedule a new one right away.
+ if (mEvaluateCompleteFuture != null && !mEvaluateCompleteFuture.isDone()) {
+ mEvaluateCompleteFuture.thenRunAsync(
+ () -> scheduleReevaluateNow(onDoneFuture), mExecutorService);
+ return;
+ }
+
+ reevaluateDelegates();
+ mEvaluateCompleteFuture.thenAccept(onDoneFuture::complete);
+ }
+
+ /**
+ * Apply all operations that have been pending by collecting pending create/destroy operations
+ * and batch applying them to the mDelegatePriorityQueue.
+ *
+ * First perform the operation of destroying all SipDelegateConnections that have been pending
+ * destroy. Next, add all pending new SipDelegateControllers to the end of
+ * mDelegatePriorityQueue and loop through all in the queue, applying feature tags to the
+ * appropriate SipDelegateController if they pass role checks and have not already been claimed
+ * by another delegate higher in the priority queue.
+ */
+ private void reevaluateDelegates() {
+ if (mEvaluateCompleteFuture != null && !mEvaluateCompleteFuture.isDone()) {
+ logw("reevaluateDelegates: last evaluate not done, deferring new request");
+ // Defer re-evaluate until after the pending re-evaluate is complete.
+ mEvaluateCompleteFuture.thenRunAsync(this::scheduleThrottledReevaluate,
+ mExecutorService);
+ return;
+ }
+
+ // Destroy all pending destroy delegates first. Order doesn't matter.
+ List<CompletableFuture<Void>> pendingDestroyList = mDelegatePendingDestroy.stream()
+ .map(d -> triggerDestroy(d.controller, d.reason)).collect(
+ Collectors.toList());
+ CompletableFuture<Void> pendingDestroy = CompletableFuture.allOf(
+ pendingDestroyList.toArray(new CompletableFuture[mDelegatePendingDestroy.size()]));
+ mDelegatePriorityQueue.removeAll(mDelegatePendingDestroy.stream().map(d -> d.controller)
+ .collect(Collectors.toList()));
+ mDelegatePendingDestroy.clear();
+
+ // Add newly created SipDelegates to end of queue before evaluating associated features.
+ mDelegatePriorityQueue.addAll(mDelegatePendingCreate);
+ for (SipDelegateController c : mDelegatePendingCreate) {
+ logi("reevaluateDelegates: pending create: " + c);
+ }
+ mDelegatePendingCreate.clear();
+
+ // Wait for destroy stages to complete, then loop from oldest to most recent and associate
+ // feature tags that the app has requested to the SipDelegate.
+ // Each feature tag can only be associated with one SipDelegate, so as feature tags are
+ // taken, do not allow other SipDelegates to be associated with those tags as well. Each
+ // stage of the CompletableFuture chain passes the previously claimed feature tags into the
+ // next stage so that those feature tags can be denied if already claimed.
+ // Executor doesn't matter here, just composing here to transform to the next stage.
+ CompletableFuture<Set<String>> pendingChange = pendingDestroy.thenCompose((ignore) -> {
+ logi("reevaluateDelegates: destroy phase complete");
+ return CompletableFuture.completedFuture(new ArraySet<>());
+ });
+ final String cachedSmsRolePackage = mCachedSmsRolePackageName;
+ for (SipDelegateController c : mDelegatePriorityQueue) {
+ logi("reevaluateDelegates: pending reeval: " + c);
+ pendingChange = pendingChange.thenComposeAsync((takenTags) -> {
+ logi("reevaluateDelegates: last stage completed with result:" + takenTags);
+ if (takenTags == null) {
+ // return early, the ImsService is no longer available. This will eventually be
+ // destroyed.
+ return CompletableFuture.completedFuture(null /*failed*/);
+ }
+ return changeSupportedFeatureTags(c, cachedSmsRolePackage, takenTags);
+ }, mExecutorService);
+ }
+
+ // Executor doesn't matter here, just adding an extra stage to print result.
+ mEvaluateCompleteFuture = pendingChange
+ .thenAccept((associatedFeatures) -> logi("reevaluateDelegates: reevaluate complete,"
+ + " feature tags associated: " + associatedFeatures));
+ logi("reevaluateDelegates: future created.");
+ }
+
+ private CompletableFuture<Void> triggerDestroy(SipDelegateController c, int reason) {
+ return c.destroy(isForcedFromReason(reason), reason)
+ // Executor doesn't matter here, just for logging.
+ .thenAccept((delegateReason) -> logi("destroy triggered with " + reason
+ + " and finished with reason= " + delegateReason));
+ }
+
+ private boolean isForcedFromReason(int reason) {
+ switch (reason) {
+ case SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_UNKNOWN:
+ logw("isForcedFromReason, unknown reason");
+ /*intentional fallthrough*/
+ case SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP:
+ /*intentional fallthrough*/
+ case SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS:
+ return false;
+ case SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD:
+ /*intentional fallthrough*/
+ case SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN:
+ return true;
+ }
+ logw("isForcedFromReason, unexpected reason: " + reason);
+ return false;
+ }
+
+ /**
+ * Called by RoleManager when a role has changed so that we can query the new role holder.
+ * @param roleName the name of the role whose holders are changed
+ * @param user the user for this role holder change
+ */
+ // Called on mExecutorThread
+ @Override
+ public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) {
+ logi("onRoleHoldersChanged, roleName= " + roleName + ", user= " + user);
+ // Only monitor changes on the system
+ if (!UserHandle.SYSTEM.equals(user)) {
+ return;
+ }
+
+ if (!RoleManager.ROLE_SMS.equals(roleName)) {
+ logi("onRoleHoldersChanged, ignoring non SMS role change");
+ // TODO: only target default sms app for now and add new roles later using
+ // CarrierConfigManager
+ return;
+ }
+ updateRoleCache();
+ // new denied tags will be picked up when reevaluate completes.
+ scheduleThrottledReevaluate();
+ }
+
+
+ private void updateRoleCache() {
+ String newSmsRolePackageName = "";
+ try {
+ // Only one app can fulfill the SMS role.
+ newSmsRolePackageName = mRoleManagerAdapter.getRoleHolders(RoleManager.ROLE_SMS)
+ .stream().findFirst().orElse("");
+ } catch (Exception e) {
+ logi("updateRoleCache: exception=" + e);
+ }
+
+ logi("updateRoleCache: new packageName=" + newSmsRolePackageName);
+ if (TextUtils.equals(mCachedSmsRolePackageName, newSmsRolePackageName)) {
+ logi("updateRoleCache, skipping, role did not change");
+ return;
+ }
+ mCachedSmsRolePackageName = newSmsRolePackageName;
+ }
+
+ /**
+ * Check the requested roles for the specified package name and return the tags that were
+ * applied to that SipDelegateController.
+ * @param controller Controller to attribute feature tags to.
+ * @param alreadyRequestedTags The feature tags that were already granted to other SipDelegates.
+ * @return Once complete, contains the set of feature tags that the SipDelegate now has
+ * associated with it along with the feature tags that previous SipDelegates had.
+ *
+ * // TODO: we currently only track SMS role, extend to support other roles as well.
+ */
+ private CompletableFuture<Set<String>> changeSupportedFeatureTags(
+ SipDelegateController controller, String smsRolePackageName,
+ Set<String> alreadyRequestedTags) {
+ Set<String> requestedFeatureTags = controller.getInitialRequest().getFeatureTags();
+ String packageName = controller.getPackageName();
+ if (!smsRolePackageName.equals(packageName)) {
+ // Deny all tags.
+ Set<FeatureTagState> deniedTags = new ArraySet<>();
+ for (String s : requestedFeatureTags) {
+ deniedTags.add(new FeatureTagState(s,
+ SipDelegateManager.DENIED_REASON_NOT_ALLOWED));
+ }
+ CompletableFuture<Boolean> pendingDeny = controller.changeSupportedFeatureTags(
+ Collections.emptySet(), deniedTags);
+ logi("changeSupportedFeatureTags pendingDeny=" + pendingDeny);
+ // do not worry about executor used here, this stage used to interpret result + add log.
+ return pendingDeny.thenApply((completedSuccessfully) -> {
+ logi("changeSupportedFeatureTags: deny completed: " + completedSuccessfully);
+ if (!completedSuccessfully) return null;
+ // Return back the previous list of requested tags, as we did not add any more.
+ return alreadyRequestedTags;
+ });
+ }
+
+ ArraySet<String> previouslyGrantedTags = new ArraySet<>(alreadyRequestedTags);
+ // deny tags already used by other delegates
+ Set<FeatureTagState> deniedTags = new ArraySet<>();
+ for (String s : requestedFeatureTags) {
+ if (previouslyGrantedTags.contains(s)) {
+ deniedTags.add(new FeatureTagState(s,
+ SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE));
+ }
+ }
+ Set<String> nonDeniedTags = requestedFeatureTags.stream()
+ .filter(r -> !previouslyGrantedTags.contains(r))
+ .collect(Collectors.toSet());
+ // Add newly granted tags to the already requested tags list.
+ previouslyGrantedTags.addAll(nonDeniedTags);
+ CompletableFuture<Boolean> pendingChange = controller.changeSupportedFeatureTags(
+ nonDeniedTags, deniedTags);
+ logi("changeSupportedFeatureTags pendingChange=" + pendingChange);
+ // do not worry about executor used here, this stage used to interpret result + add log.
+ return pendingChange.thenApply((completedSuccessfully) -> {
+ logi("changeSupportedFeatureTags: change completed: " + completedSuccessfully);
+ if (!completedSuccessfully) return null;
+ return previouslyGrantedTags;
+ });
+ }
+
+ /**
+ * Run a Callable on the ExecutorService Thread and wait for the result.
+ * If an ImsException is thrown, catch it and rethrow it to caller.
+ */
+ private <T> T waitForMethodToComplete(Callable<T> callable) throws ImsException {
+ Future<T> r = mExecutorService.submit(callable);
+ T result;
+ try {
+ result = r.get();
+ } catch (InterruptedException e) {
+ result = null;
+ } catch (ExecutionException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof ImsException) {
+ // Rethrow the exception
+ throw (ImsException) cause;
+ }
+ logw("Unexpected Exception, returning null: " + cause);
+ result = null;
+ }
+ return result;
+ }
+
+ /**
+ * Throw an ImsException for common scenarios where the state of the controller is not ready
+ * for communication.
+ * <p>
+ * This should only be called while running on the on the ExecutorService.
+ */
+ private void checkStateOfController(int subId) throws ImsException {
+ if (mSubId != subId) {
+ // sub ID has changed while this was in the queue.
+ throw new ImsException("subId is no longer valid for this request.",
+ ImsException.CODE_ERROR_INVALID_SUBSCRIPTION);
+ }
+ if (mRcsManager == null) {
+ throw new ImsException("Connection to ImsService is not available",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ private void onRcsManagerChanged(RcsFeatureManager m) {
+ logi("manager changed, " + mRcsManager + "->" + m);
+ if (mRcsManager == m) return;
+ mRcsManager = m;
+ if (mRcsManager == null) {
+ logi("onRcsManagerChanged: lost connection to ImsService, tearing down...");
+ unregisterListeners();
+ scheduleDestroyDelegates(SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+ } else {
+ logi("onRcsManagerChanged: registering listeners/updating role cache...");
+ registerListeners();
+ updateRoleCache();
+ }
+ }
+
+ private void registerListeners() {
+ try {
+ mRoleManagerAdapter.addOnRoleHoldersChangedListenerAsUser(mExecutorService, this,
+ UserHandle.SYSTEM);
+ } catch (Exception e) {
+ logi("registerListeners: exception=" + e);
+ }
+ }
+
+ private void unregisterListeners() {
+ mCachedSmsRolePackageName = "";
+ mRoleManagerAdapter.removeOnRoleHoldersChangedListenerAsUser(this, UserHandle.SYSTEM);
+ }
+
+ /**
+ * Called when either the sub ID associated with the slot has changed or the carrier
+ * configuration associated with the same subId has changed.
+ */
+ private void onSubIdChanged(int newSubId) {
+ logi("subId changed, " + mSubId + "->" + newSubId);
+ if (mSubId != newSubId) {
+ // Swap subId, any pending create/destroy on old subId will be denied.
+ mSubId = newSubId;
+ scheduleDestroyDelegates(
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
+ return;
+ }
+ // TODO: if subId hasn't changed this means that we should load in any new carrier configs
+ // that we care about and apply.
+ }
+
+ /**
+ * Destroy all tracked SipDelegateConnections due to the subscription being torn down.
+ * @return A CompletableFuture that will complete when all SipDelegates have been torn down.
+ */
+ private CompletableFuture<Void> scheduleDestroyDelegates(int reason) {
+ boolean addedDestroy = false;
+ for (SipDelegateController c : mDelegatePriorityQueue) {
+ logi("scheduleDestroyDelegates: Controller pending destroy: " + c);
+ addedDestroy |= addPendingDestroy(c, reason);
+ }
+ if (addedDestroy) {
+ CompletableFuture<Void> pendingDestroy = new CompletableFuture<>();
+ scheduleReevaluateNow(pendingDestroy);
+ return pendingDestroy;
+ } else {
+ return CompletableFuture.completedFuture(null);
+ }
+ }
+
+ @Override
+ public void dump(PrintWriter printWriter) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ pw.println("SipTransportController" + "[" + mSlotId + "->" + mSubId + "]:");
+ pw.increaseIndent();
+ pw.println("LocalLog:");
+ pw.increaseIndent();
+ mLocalLog.dump(pw);
+ pw.decreaseIndent();
+ pw.println("SipDelegateControllers (in priority order):");
+ pw.increaseIndent();
+ if (mDelegatePriorityQueue.isEmpty()) {
+ pw.println("[NONE]");
+ } else {
+ for (SipDelegateController c : mDelegatePriorityQueue) {
+ c.dump(pw);
+ }
+ }
+ pw.decreaseIndent();
+ pw.decreaseIndent();
+ }
+
+ private void logi(String log) {
+ Log.w(LOG_TAG, "[" + mSlotId + "->" + mSubId + "] " + log);
+ mLocalLog.log("[I] " + log);
+ }
+
+ private void logw(String log) {
+ Log.w(LOG_TAG, "[" + mSlotId + "->" + mSubId + "] " + log);
+ mLocalLog.log("[W] " + log);
+ }
+}
diff --git a/src/com/android/services/telephony/rcs/TelephonyRcsService.java b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
index 69d8f82..941a6a8 100644
--- a/src/com/android/services/telephony/rcs/TelephonyRcsService.java
+++ b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
@@ -47,21 +47,25 @@
private static final String LOG_TAG = "TelephonyRcsService";
/**
- * Used to inject RcsFeatureController and UserCapabilityExchangeImpl instances for testing.
+ * Used to inject RcsFeatureController and UceController instances for testing.
*/
@VisibleForTesting
public interface FeatureFactory {
/**
- * @return an {@link RcsFeatureController} assoicated with the slot specified.
+ * @return an {@link RcsFeatureController} associated with the slot specified.
*/
RcsFeatureController createController(Context context, int slotId);
/**
- * @return an instance of {@link UserCapabilityExchangeImpl} associated with the slot
+ * @return an instance of {@link UceControllerManager} associated with the slot specified.
+ */
+ UceControllerManager createUceControllerManager(Context context, int slotId, int subId);
+
+ /**
+ * @return an instance of {@link SipTransportController} for the slot and subscription
* specified.
*/
- UserCapabilityExchangeImpl createUserCapabilityExchange(Context context, int slotId,
- int subId);
+ SipTransportController createSipTransportController(Context context, int slotId, int subId);
}
private FeatureFactory mFeatureFactory = new FeatureFactory() {
@@ -71,9 +75,15 @@
}
@Override
- public UserCapabilityExchangeImpl createUserCapabilityExchange(Context context, int slotId,
+ public UceControllerManager createUceControllerManager(Context context, int slotId,
int subId) {
- return new UserCapabilityExchangeImpl(context, slotId, subId);
+ return new UceControllerManager(context, slotId, subId);
+ }
+
+ @Override
+ public SipTransportController createSipTransportController(Context context, int slotId,
+ int subId) {
+ return new SipTransportController(context, slotId, subId);
}
};
@@ -225,13 +235,24 @@
private void updateSupportedFeatures(RcsFeatureController c, int slotId, int subId) {
if (doesSubscriptionSupportPresence(subId)) {
- if (c.getFeature(UserCapabilityExchangeImpl.class) == null) {
- c.addFeature(mFeatureFactory.createUserCapabilityExchange(mContext, slotId, subId),
- UserCapabilityExchangeImpl.class);
+ if (c.getFeature(UceControllerManager.class) == null) {
+ c.addFeature(mFeatureFactory.createUceControllerManager(mContext, slotId, subId),
+ UceControllerManager.class);
}
} else {
- if (c.getFeature(UserCapabilityExchangeImpl.class) != null) {
- c.removeFeature(UserCapabilityExchangeImpl.class);
+ if (c.getFeature(UceControllerManager.class) != null) {
+ c.removeFeature(UceControllerManager.class);
+ }
+ }
+
+ if (doesSubscriptionSupportSingleRegistration(subId)) {
+ if (c.getFeature(SipTransportController.class) == null) {
+ c.addFeature(mFeatureFactory.createSipTransportController(mContext, slotId, subId),
+ SipTransportController.class);
+ }
+ } else {
+ if (c.getFeature(SipTransportController.class) != null) {
+ c.removeFeature(SipTransportController.class);
}
}
// Only start the connection procedure if we have active features.
@@ -250,6 +271,14 @@
return supportsUce;
}
+ private boolean doesSubscriptionSupportSingleRegistration(int subId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) return false;
+ CarrierConfigManager carrierConfigManager =
+ mContext.getSystemService(CarrierConfigManager.class);
+ if (carrierConfigManager == null) return false;
+ return carrierConfigManager.getConfigForSubId(subId).getBoolean(
+ CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL);
+ }
private int getSubscriptionFromSlot(int slotId) {
SubscriptionManager manager = mContext.getSystemService(SubscriptionManager.class);
diff --git a/src/com/android/services/telephony/rcs/UceControllerManager.java b/src/com/android/services/telephony/rcs/UceControllerManager.java
new file mode 100644
index 0000000..d1f91d1
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/UceControllerManager.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2020 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 com.android.services.telephony.rcs;
+
+import android.content.Context;
+import android.net.Uri;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.RcsUceAdapter.PublishState;
+import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
+import android.util.Log;
+
+import com.android.ims.RcsFeatureManager;
+import com.android.ims.rcs.uce.UceController;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+/**
+ * Responsible for managing the creation and destruction of UceController. It also received the
+ * requests from {@link com.android.phone.ImsRcsController} and pass these requests to
+ * {@link UceController}
+ */
+public class UceControllerManager implements RcsFeatureController.Feature {
+
+ private static final String LOG_TAG = "UceControllerManager";
+
+ private final int mSlotId;
+ private final Context mContext;
+ private final ExecutorService mExecutorService;
+
+ private volatile UceController mUceController;
+
+ public UceControllerManager(Context context, int slotId, int subId) {
+ Log.d(LOG_TAG, "create: slotId=" + slotId + ", subId=" + subId);
+
+ mSlotId = slotId;
+ mContext = context;
+ mExecutorService = Executors.newSingleThreadExecutor();
+ mUceController = new UceController(mContext, subId);
+ }
+
+ /**
+ * Constructor to inject dependencies for testing.
+ */
+ @VisibleForTesting
+ public UceControllerManager(Context context, int slotId, int subId, ExecutorService executor) {
+ mSlotId = slotId;
+ mContext = context;
+ mExecutorService = executor;
+ mUceController = new UceController(mContext, subId);
+ }
+
+ @Override
+ public void onRcsConnected(RcsFeatureManager manager) {
+ mExecutorService.submit(() -> mUceController.onRcsConnected(manager));
+ }
+
+ @Override
+ public void onRcsDisconnected() {
+ mExecutorService.submit(() -> mUceController.onRcsDisconnected());
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.d(LOG_TAG, "onDestroy");
+ mExecutorService.submit(() -> mUceController.onDestroy());
+ // When the shutdown is called, it will refuse any new tasks and let existing tasks finish.
+ mExecutorService.shutdown();
+ }
+
+ /**
+ * This method will be called when either the subscription ID associated with the slot has
+ * changed or the carrier configuration associated with the same subId has changed.
+ */
+ @Override
+ public void onAssociatedSubscriptionUpdated(int subId) {
+ mExecutorService.submit(() -> {
+ Log.i(LOG_TAG, "onAssociatedSubscriptionUpdated: slotId=" + mSlotId
+ + ", subId=" + subId);
+
+ // Destroy existing UceController and create a new one.
+ mUceController.onDestroy();
+ mUceController = new UceController(mContext, subId);
+ });
+ }
+
+ @VisibleForTesting
+ public void setUceController(UceController uceController) {
+ mUceController = uceController;
+ }
+
+ /**
+ * Request the capabilities for contacts.
+ *
+ * @param contactNumbers A list of numbers that the capabilities are being requested for.
+ * @param c A callback for when the request for capabilities completes.
+ * @throws ImsException if the ImsService connected to this controller is currently down.
+ */
+ public void requestCapabilities(List<Uri> contactNumbers, IRcsUceControllerCallback c)
+ throws ImsException {
+ Future future = mExecutorService.submit(() -> {
+ checkUceControllerState();
+ mUceController.requestCapabilities(contactNumbers, c);
+ return true;
+ });
+
+ try {
+ future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "requestCapabilities: " + e);
+ Throwable cause = e.getCause();
+ if (cause instanceof ImsException) {
+ throw (ImsException) cause;
+ }
+ }
+ }
+
+ /**
+ * Request the capabilities for the given contact.
+ * @param contactNumber The contact of the capabilities are being requested for.
+ * @param c A callback for when the request for capabilities completes.
+ * @throws ImsException if the ImsService connected to this controller is currently down.
+ */
+ public void requestNetworkAvailability(Uri contactNumber, IRcsUceControllerCallback c)
+ throws ImsException {
+ Future future = mExecutorService.submit(() -> {
+ checkUceControllerState();
+ mUceController.requestAvailability(contactNumber, c);
+ return true;
+ });
+
+ try {
+ future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "requestNetworkAvailability exception: " + e);
+ Throwable cause = e.getCause();
+ if (cause instanceof ImsException) {
+ throw (ImsException) cause;
+ }
+ }
+ }
+
+ /**
+ * Get the UCE publish state.
+ *
+ * @throws ImsException if the ImsService connected to this controller is currently down.
+ */
+ public @PublishState int getUcePublishState() throws ImsException {
+ Future future = mExecutorService.submit(() -> {
+ checkUceControllerState();
+ return mUceController.getUcePublishState();
+ });
+
+ try {
+ return (Integer) future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "requestNetworkAvailability exception: " + e);
+ Throwable cause = e.getCause();
+ if (cause instanceof ImsException) {
+ throw (ImsException) cause;
+ }
+ return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
+ }
+ }
+
+ /**
+ * Register the Publish state changed callback.
+ *
+ * @throws ImsException if the ImsService connected to this controller is currently down.
+ */
+ public void registerPublishStateCallback(IRcsUcePublishStateCallback c) throws ImsException {
+ Future future = mExecutorService.submit(() -> {
+ checkUceControllerState();
+ mUceController.registerPublishStateCallback(c);
+ return true;
+ });
+
+ try {
+ future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "registerPublishStateCallback exception: " + e);
+ Throwable cause = e.getCause();
+ if (cause instanceof ImsException) {
+ throw (ImsException) cause;
+ }
+ }
+ }
+
+ /**
+ * Unregister the existing publish state changed callback.
+ */
+ public void unregisterPublishStateCallback(IRcsUcePublishStateCallback c) {
+ Future future = mExecutorService.submit(() -> {
+ if (checkUceControllerState()) {
+ mUceController.unregisterPublishStateCallback(c);
+ }
+ return true;
+ });
+
+ try {
+ future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "unregisterPublishStateCallback exception: " + e);
+ }
+ }
+
+ private boolean checkUceControllerState() throws ImsException {
+ if (mUceController == null || mUceController.isUnavailable()) {
+ throw new ImsException("UCE controller is unavailable",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ return true;
+ }
+
+
+ @Override
+ public void dump(PrintWriter printWriter) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ pw.println("UceControllerManager" + "[" + mSlotId + "]:");
+ pw.increaseIndent();
+ pw.println("UceController available = " + mUceController != null);
+ //TODO: Add dump for UceController
+ pw.decreaseIndent();
+ }
+}
diff --git a/src/com/android/services/telephony/rcs/UserCapabilityExchangeImpl.java b/src/com/android/services/telephony/rcs/UserCapabilityExchangeImpl.java
deleted file mode 100644
index ee0c5be..0000000
--- a/src/com/android/services/telephony/rcs/UserCapabilityExchangeImpl.java
+++ /dev/null
@@ -1,1065 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.services.telephony.rcs;
-
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PersistableBundle;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.provider.Settings;
-import android.provider.Telephony;
-import android.telecom.TelecomManager;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
-import android.telephony.ims.ImsException;
-import android.telephony.ims.ImsManager;
-import android.telephony.ims.ImsMmTelManager;
-import android.telephony.ims.ImsReasonInfo;
-import android.telephony.ims.ProvisioningManager;
-import android.telephony.ims.RcsContactUceCapability;
-import android.telephony.ims.RcsUceAdapter;
-import android.telephony.ims.RegistrationManager;
-import android.telephony.ims.aidl.IRcsUceControllerCallback;
-import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
-import android.telephony.ims.feature.MmTelFeature;
-import android.telephony.ims.stub.RcsCapabilityExchange;
-import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
-import android.util.Log;
-
-import com.android.ims.RcsFeatureManager;
-import com.android.ims.RcsFeatureManager.RcsFeatureCallbacks;
-import com.android.ims.ResultCode;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.phone.R;
-import com.android.service.ims.presence.ContactCapabilityResponse;
-import com.android.service.ims.presence.PresenceBase;
-import com.android.service.ims.presence.PresencePublication;
-import com.android.service.ims.presence.PresencePublisher;
-import com.android.service.ims.presence.PresenceSubscriber;
-import com.android.service.ims.presence.SubscribePublisher;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
-
-/**
- * Implements User Capability Exchange using Presence.
- */
-public class UserCapabilityExchangeImpl implements RcsFeatureController.Feature, SubscribePublisher,
- PresencePublisher {
-
- private static final String LOG_TAG = "RcsUceImpl";
-
- private final int mSlotId;
- private volatile int mSubId;
- private volatile boolean mImsContentChangedCallbackRegistered = false;
- // The result of requesting publish
- private volatile int mPublishState = PresenceBase.PUBLISH_STATE_NOT_PUBLISHED;
- // The network type which IMS registers on
- private volatile int mNetworkRegistrationType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
- // The MMTel capabilities of this subscription Id
- private MmTelFeature.MmTelCapabilities mMmTelCapabilities;
- private final Object mCapabilitiesLock = new Object();
-
- private final Context mContext;
- private final UceImplHandler mUceImplHandler;
- private RcsFeatureManager mRcsFeatureManager;
- private final PresencePublication mPresencePublication;
- private final PresenceSubscriber mPresenceSubscriber;
-
- // The task Ids of updating capabilities
- private final Set<Integer> mRequestingPublishTaskIds = new HashSet<>();
-
- // The callbacks to notify publish state changed.
- private final RemoteCallbackList<IRcsUcePublishStateCallback> mPublishStateCallbacks;
-
- // The task Ids of pending availability request.
- private final Set<Integer> mPendingAvailabilityRequests = new HashSet<>();
-
- private final ConcurrentHashMap<Integer, IRcsUceControllerCallback> mPendingCapabilityRequests =
- new ConcurrentHashMap<>();
-
- UserCapabilityExchangeImpl(Context context, int slotId, int subId) {
- mSlotId = slotId;
- mSubId = subId;
- logi("created");
-
- mContext = context;
- mPublishStateCallbacks = new RemoteCallbackList<>();
-
- HandlerThread handlerThread = new HandlerThread("UceImplHandlerThread");
- handlerThread.start();
- mUceImplHandler = new UceImplHandler(this, handlerThread.getLooper());
-
- String[] volteError = context.getResources().getStringArray(
- R.array.config_volte_provision_error_on_publish_response);
- String[] rcsError = context.getResources().getStringArray(
- R.array.config_rcs_provision_error_on_publish_response);
-
- // Initialize PresencePublication
- mPresencePublication = new PresencePublication(null /*PresencePublisher*/, context,
- volteError, rcsError);
- // Initialize PresenceSubscriber
- mPresenceSubscriber = new PresenceSubscriber(null /*SubscribePublisher*/, context,
- volteError, rcsError);
-
- onAssociatedSubscriptionUpdated(mSubId);
- registerReceivers();
- }
-
- @VisibleForTesting
- UserCapabilityExchangeImpl(Context context, int slotId, int subId, Looper looper,
- PresencePublication presencePublication, PresenceSubscriber presenceSubscriber,
- RemoteCallbackList<IRcsUcePublishStateCallback> publishStateCallbacks) {
- mSlotId = slotId;
- mSubId = subId;
- mContext = context;
- mPublishStateCallbacks = publishStateCallbacks;
- mUceImplHandler = new UceImplHandler(this, looper);
- mPresencePublication = presencePublication;
- mPresenceSubscriber = presenceSubscriber;
- onAssociatedSubscriptionUpdated(mSubId);
- registerReceivers();
- }
-
- // Runs on main thread.
- @Override
- public void onRcsConnected(RcsFeatureManager rcsFeatureManager) {
- logi("onRcsConnected");
- mRcsFeatureManager = rcsFeatureManager;
- mRcsFeatureManager.addFeatureListenerCallback(mRcsFeatureCallback);
-
- mPresencePublication.updatePresencePublisher(this);
- mPresenceSubscriber.updatePresenceSubscriber(this);
- }
-
- // Runs on main thread.
- @Override
- public void onRcsDisconnected() {
- logi("onRcsDisconnected");
- mPresencePublication.removePresencePublisher();
- mPresenceSubscriber.removePresenceSubscriber();
-
- if (mRcsFeatureManager != null) {
- mRcsFeatureManager.releaseConnection();
- mRcsFeatureManager = null;
- }
- }
-
- // Runs on main thread.
- @Override
- public void onAssociatedSubscriptionUpdated(int subId) {
- logi("onAssociatedSubscriptionUpdated: new subId=" + subId);
-
- // Listen to the IMS content changed with new subId.
- mUceImplHandler.registerImsContentChangedReceiver(subId);
-
- mSubId = subId;
- mPresencePublication.handleAssociatedSubscriptionChanged(subId);
- mPresenceSubscriber.handleAssociatedSubscriptionChanged(subId);
- }
-
- /**
- * Should be called before destroying this instance.
- * This instance is not usable after this method is called.
- */
- // Called on main thread.
- public void onDestroy() {
- logi("onDestroy");
- mUceImplHandler.getLooper().quit();
- unregisterReceivers();
- unregisterImsProvisionCallback(mSubId);
- onRcsDisconnected();
- }
-
- /**
- * @return the UCE Publish state.
- */
- // May happen on a Binder thread, PresencePublication locks to get result.
- public int getUcePublishState() {
- int publishState = mPresencePublication.getPublishState();
- return toUcePublishState(publishState);
- }
-
- @VisibleForTesting
- public UceImplHandler getHandler() {
- return mUceImplHandler;
- }
-
- /**
- * Register receiver to receive UCE publish state changed.
- */
- public void registerPublishStateCallback(IRcsUcePublishStateCallback c) {
- synchronized (mPublishStateCallbacks) {
- mPublishStateCallbacks.register(c);
- }
- }
-
- /**
- * Unregister UCE publish state callback.
- */
- public void unregisterUcePublishStateCallback(IRcsUcePublishStateCallback c) {
- synchronized (mPublishStateCallbacks) {
- mPublishStateCallbacks.unregister(c);
- }
- }
-
- private void clearPublishStateCallbacks() {
- synchronized (mPublishStateCallbacks) {
- logi("clearPublishStateCallbacks");
- final int lastIndex = mPublishStateCallbacks.getRegisteredCallbackCount() - 1;
- for (int index = lastIndex; index >= 0; index--) {
- IRcsUcePublishStateCallback callback =
- mPublishStateCallbacks.getRegisteredCallbackItem(index);
- mPublishStateCallbacks.unregister(callback);
- }
- }
- }
-
- private void notifyPublishStateChanged(@PresenceBase.PresencePublishState int state) {
- int result = toUcePublishState(state);
- synchronized (mPublishStateCallbacks) {
- mPublishStateCallbacks.broadcast(c -> {
- try {
- c.onPublishStateChanged(result);
- } catch (RemoteException e) {
- logw("notifyPublishStateChanged error: " + e);
- }
- });
- }
- }
-
- /**
- * Perform a capabilities request and call {@link IRcsUceControllerCallback} with the result.
- */
- // May happen on a Binder thread, PresenceSubscriber locks when requesting Capabilities.
- public void requestCapabilities(List<Uri> contactNumbers, IRcsUceControllerCallback c) {
- List<String> numbers = contactNumbers.stream()
- .map(UserCapabilityExchangeImpl::getNumberFromUri).collect(Collectors.toList());
- int taskId = mPresenceSubscriber.requestCapability(numbers,
- new ContactCapabilityResponse() {
- @Override
- public void onSuccess(int reqId) {
- logi("onSuccess called for reqId:" + reqId);
- }
-
- @Override
- public void onError(int reqId, int resultCode) {
- IRcsUceControllerCallback c = mPendingCapabilityRequests.remove(reqId);
- try {
- if (c != null) {
- c.onError(toUceError(resultCode));
- } else {
- logw("onError called for unknown reqId:" + reqId);
- }
- } catch (RemoteException e) {
- logi("Calling back to dead service");
- }
- }
-
- @Override
- public void onFinish(int reqId) {
- logi("onFinish called for reqId:" + reqId);
- }
-
- @Override
- public void onTimeout(int reqId) {
- IRcsUceControllerCallback c = mPendingCapabilityRequests.remove(reqId);
- try {
- if (c != null) {
- c.onError(RcsUceAdapter.ERROR_REQUEST_TIMEOUT);
- } else {
- logw("onTimeout called for unknown reqId:" + reqId);
- }
- } catch (RemoteException e) {
- logi("Calling back to dead service");
- }
- }
-
- @Override
- public void onCapabilitiesUpdated(int reqId,
- List<RcsContactUceCapability> contactCapabilities,
- boolean updateLastTimestamp) {
- IRcsUceControllerCallback c = mPendingCapabilityRequests.remove(reqId);
- try {
- if (c != null) {
- c.onCapabilitiesReceived(contactCapabilities);
- } else {
- logw("onCapabilitiesUpdated, unknown reqId:" + reqId);
- }
- } catch (RemoteException e) {
- logw("onCapabilitiesUpdated on dead service");
- }
- }
- });
- if (taskId < 0) {
- try {
- c.onError(toUceError(taskId));
- } catch (RemoteException e) {
- logi("Calling back to dead service");
- }
- return;
- }
- mPendingCapabilityRequests.put(taskId, c);
- }
-
- @Override
- public int requestCapability(String[] formattedContacts, int taskId) {
- if (formattedContacts == null || formattedContacts.length == 0) {
- logw("requestCapability error: contacts is null.");
- return ResultCode.SUBSCRIBE_INVALID_PARAM;
- }
- if (mRcsFeatureManager == null) {
- logw("requestCapability error: RcsFeatureManager is null.");
- return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
- }
-
- logi("requestCapability: taskId=" + taskId);
-
- try {
- List<Uri> contactList = Arrays.stream(formattedContacts)
- .map(Uri::parse).collect(Collectors.toList());
- mRcsFeatureManager.requestCapabilities(contactList, taskId);
- } catch (Exception e) {
- logw("requestCapability error: " + e.getMessage());
- return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
- }
- return ResultCode.SUCCESS;
- }
-
- @Override
- public int requestAvailability(String formattedContact, int taskId) {
- if (formattedContact == null || formattedContact.isEmpty()) {
- logw("requestAvailability error: contact is null.");
- return ResultCode.SUBSCRIBE_INVALID_PARAM;
- }
- if (mRcsFeatureManager == null) {
- logw("requestAvailability error: RcsFeatureManager is null.");
- return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
- }
-
- logi("requestAvailability: taskId=" + taskId);
- addRequestingAvailabilityTaskId(taskId);
-
- try {
- Uri contactUri = Uri.parse(formattedContact);
- List<Uri> contactUris = new ArrayList<>(Arrays.asList(contactUri));
- mRcsFeatureManager.requestCapabilities(contactUris, taskId);
- } catch (Exception e) {
- logw("requestAvailability error: " + e.getMessage());
- removeRequestingAvailabilityTaskId(taskId);
- return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
- }
- return ResultCode.SUCCESS;
- }
-
- @Override
- public int getStackStatusForCapabilityRequest() {
- if (mRcsFeatureManager == null) {
- logw("Check Stack status: Error! RcsFeatureManager is null.");
- return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
- }
-
- if (!isCapabilityDiscoveryEnabled(mSubId)) {
- logw("Check Stack status: Error! capability discovery not enabled");
- return ResultCode.ERROR_SERVICE_NOT_ENABLED;
- }
-
- if (!isEabProvisioned(mContext, mSubId)) {
- logw("Check Stack status: Error! EAB provisioning disabled.");
- return ResultCode.ERROR_SERVICE_NOT_ENABLED;
- }
-
- if (getPublisherState() != PresenceBase.PUBLISH_STATE_200_OK) {
- logw("Check Stack status: Error! publish state " + getPublisherState());
- return ResultCode.ERROR_SERVICE_NOT_PUBLISHED;
- }
- return ResultCode.SUCCESS;
- }
-
- /**
- * The feature callback is to receive the request and update from RcsPresExchangeImplBase
- */
- @VisibleForTesting
- public RcsFeatureCallbacks mRcsFeatureCallback = new RcsFeatureCallbacks() {
- public void onCommandUpdate(int commandCode, int operationToken) {
- logi("onCommandUpdate: code=" + commandCode + ", token=" + operationToken);
- if (isPublishRequestExisted(operationToken)) {
- onCommandUpdateForPublishRequest(commandCode, operationToken);
- } else if (isCapabilityRequestExisted(operationToken)) {
- onCommandUpdateForCapabilityRequest(commandCode, operationToken);
- } else if (isAvailabilityRequestExisted(operationToken)) {
- onCommandUpdateForAvailabilityRequest(commandCode, operationToken);
- } else {
- logw("onCommandUpdate: invalid token " + operationToken);
- }
- }
-
- /** See {@link RcsPresenceExchangeImplBase#onNetworkResponse(int, String, int)} */
- public void onNetworkResponse(int responseCode, String reason, int operationToken) {
- logi("onNetworkResponse: code=" + responseCode + ", reason=" + reason
- + ", operationToken=" + operationToken);
- if (isPublishRequestExisted(operationToken)) {
- onNetworkResponseForPublishRequest(responseCode, reason, operationToken);
- } else if (isCapabilityRequestExisted(operationToken)) {
- onNetworkResponseForCapabilityRequest(responseCode, reason, operationToken);
- } else if (isAvailabilityRequestExisted(operationToken)) {
- onNetworkResponseForAvailabilityRequest(responseCode, reason, operationToken);
- } else {
- logw("onNetworkResponse: invalid token " + operationToken);
- }
- }
-
- /** See {@link RcsPresenceExchangeImplBase#onCapabilityRequestResponse(List, int)} */
- public void onCapabilityRequestResponsePresence(List<RcsContactUceCapability> infos,
- int operationToken) {
- if (isAvailabilityRequestExisted(operationToken)) {
- handleAvailabilityReqResponse(infos, operationToken);
- } else if (isCapabilityRequestExisted(operationToken)) {
- handleCapabilityReqResponse(infos, operationToken);
- } else {
- logw("capability request response: invalid token " + operationToken);
- }
- }
-
- /** See {@link RcsPresenceExchangeImplBase#onNotifyUpdateCapabilites(int)} */
- public void onNotifyUpdateCapabilities(int publishTriggerType) {
- logi("onNotifyUpdateCapabilities: type=" + publishTriggerType);
- mUceImplHandler.notifyUpdateCapabilities(publishTriggerType);
- }
-
- /** See {@link RcsPresenceExchangeImplBase#onUnpublish()} */
- public void onUnpublish() {
- logi("onUnpublish");
- mUceImplHandler.unpublish();
- }
- };
-
- private static class UceImplHandler extends Handler {
- private static final int EVENT_REGISTER_IMS_CHANGED_RECEIVER = 1;
- private static final int EVENT_NOTIFY_UPDATE_CAPABILITIES = 2;
- private static final int EVENT_UNPUBLISH = 3;
-
- private static final int REGISTER_IMS_CHANGED_DELAY = 10000; //10 seconds
-
- private final WeakReference<UserCapabilityExchangeImpl> mUceImplRef;
-
- UceImplHandler(UserCapabilityExchangeImpl uceImpl, Looper looper) {
- super(looper);
- mUceImplRef = new WeakReference(uceImpl);
- }
-
- @Override
- public void handleMessage(Message msg) {
- UserCapabilityExchangeImpl uceImpl = mUceImplRef.get();
- if (uceImpl == null) {
- return;
- }
- switch (msg.what) {
- case EVENT_REGISTER_IMS_CHANGED_RECEIVER:
- int subId = msg.arg1;
- uceImpl.registerImsContentChangedReceiverInternal(subId);
- break;
- case EVENT_NOTIFY_UPDATE_CAPABILITIES:
- int publishTriggerType = msg.arg1;
- uceImpl.onNotifyUpdateCapabilities(publishTriggerType);
- break;
- case EVENT_UNPUBLISH:
- uceImpl.onUnPublish();
- break;
- default:
- Log.w(LOG_TAG, "handleMessage: error=" + msg.what);
- break;
- }
- }
-
- private void retryRegisteringImsContentChangedReceiver(int subId) {
- sendRegisteringImsContentChangedMessage(subId, REGISTER_IMS_CHANGED_DELAY);
- }
-
- private void registerImsContentChangedReceiver(int subId) {
- sendRegisteringImsContentChangedMessage(subId, 0);
- }
-
- private void sendRegisteringImsContentChangedMessage(int subId, int delay) {
- if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- return;
- }
- removeRegisteringImsContentChangedReceiver();
- Message message = obtainMessage(EVENT_REGISTER_IMS_CHANGED_RECEIVER);
- message.arg1 = subId;
- sendMessageDelayed(message, delay);
- }
-
- private void removeRegisteringImsContentChangedReceiver() {
- removeMessages(EVENT_REGISTER_IMS_CHANGED_RECEIVER);
- }
-
- private void notifyUpdateCapabilities(int publishTriggerType) {
- Message message = obtainMessage(EVENT_NOTIFY_UPDATE_CAPABILITIES);
- message.arg1 = publishTriggerType;
- sendMessage(message);
- }
-
- private void unpublish() {
- sendEmptyMessage(EVENT_UNPUBLISH);
- }
- }
-
- private void onNotifyUpdateCapabilities(int publishTriggerType) {
- mPresencePublication.onStackPublishRequested(publishTriggerType);
- }
-
- private void onUnPublish() {
- mPresencePublication.setPublishState(PresenceBase.PUBLISH_STATE_NOT_PUBLISHED);
- }
-
- @Override
- public @PresenceBase.PresencePublishState int getPublisherState() {
- return mPublishState;
- }
-
- @Override
- public int requestPublication(RcsContactUceCapability capabilities, String contactUri,
- int taskId) {
- if (mRcsFeatureManager == null) {
- logw("requestPublication error: RcsFeatureManager is null.");
- return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
- }
-
- logi("requestPublication: taskId=" + taskId);
- addPublishRequestTaskId(taskId);
-
- try {
- mRcsFeatureManager.requestPublication(capabilities, taskId);
- } catch (Exception ex) {
- logw("requestPublication error: " + ex.getMessage());
- removePublishRequestTaskId(taskId);
- return ResultCode.PUBLISH_GENERIC_FAILURE;
- }
- return ResultCode.SUCCESS;
- }
-
- /*
- * Handle the callback method RcsFeatureCallbacks#onCommandUpdate(int, int)
- */
- private void onCommandUpdateForPublishRequest(int commandCode, int operationToken) {
- if (!isPublishRequestExisted(operationToken)) {
- return;
- }
- int resultCode = ResultCode.SUCCESS;
- if (commandCode != RcsCapabilityExchange.COMMAND_CODE_SUCCESS) {
- logw("onCommandUpdateForPublishRequest failed! taskId=" + operationToken
- + ", code=" + commandCode);
- removePublishRequestTaskId(operationToken);
- resultCode = ResultCode.PUBLISH_GENERIC_FAILURE;
- }
- mPresencePublication.onCommandStatusUpdated(operationToken, operationToken, resultCode);
- }
-
- private void onCommandUpdateForCapabilityRequest(int commandCode, int operationToken) {
- if (!isCapabilityRequestExisted(operationToken)) {
- return;
- }
- int resultCode = ResultCode.SUCCESS;
- if (commandCode != RcsCapabilityExchange.COMMAND_CODE_SUCCESS) {
- logw("onCommandUpdateForCapabilityRequest failed! taskId=" + operationToken
- + ", code=" + commandCode);
- mPendingCapabilityRequests.remove(operationToken);
- resultCode = ResultCode.PUBLISH_GENERIC_FAILURE;
- }
- mPresenceSubscriber.onCommandStatusUpdated(operationToken, operationToken, resultCode);
- }
-
- private void onCommandUpdateForAvailabilityRequest(int commandCode, int operationToken) {
- if (!isAvailabilityRequestExisted(operationToken)) {
- return;
- }
- int resultCode = ResultCode.SUCCESS;
- if (commandCode != RcsCapabilityExchange.COMMAND_CODE_SUCCESS) {
- logw("onCommandUpdateForAvailabilityRequest failed! taskId=" + operationToken
- + ", code=" + commandCode);
- removeRequestingAvailabilityTaskId(operationToken);
- resultCode = ResultCode.PUBLISH_GENERIC_FAILURE;
- }
- mPresenceSubscriber.onCommandStatusUpdated(operationToken, operationToken, resultCode);
- }
-
- /*
- * Handle the callback method RcsFeatureCallbacks#onNetworkResponse(int, String, int)
- */
- private void onNetworkResponseForPublishRequest(int responseCode, String reason,
- int operationToken) {
- if (!isPublishRequestExisted(operationToken)) {
- return;
- }
- removePublishRequestTaskId(operationToken);
- mPresencePublication.onSipResponse(operationToken, responseCode, reason);
- }
-
- private void onNetworkResponseForCapabilityRequest(int responseCode, String reason,
- int operationToken) {
- if (!isCapabilityRequestExisted(operationToken)) {
- return;
- }
- mPresenceSubscriber.onSipResponse(operationToken, responseCode, reason);
- }
-
- private void onNetworkResponseForAvailabilityRequest(int responseCode, String reason,
- int operationToken) {
- if (!isAvailabilityRequestExisted(operationToken)) {
- return;
- }
- removeRequestingAvailabilityTaskId(operationToken);
- mPresenceSubscriber.onSipResponse(operationToken, responseCode, reason);
- }
-
- private void handleAvailabilityReqResponse(List<RcsContactUceCapability> infos, int token) {
- try {
- if (infos == null || infos.isEmpty()) {
- logw("handle availability request response: infos is null " + token);
- return;
- }
- logi("handleAvailabilityReqResponse: token=" + token);
- mPresenceSubscriber.updatePresence(infos.get(0));
- } finally {
- removeRequestingAvailabilityTaskId(token);
- }
- }
-
- private void handleCapabilityReqResponse(List<RcsContactUceCapability> infos, int token) {
- if (infos == null) {
- logw("handleCapabilityReqResponse: infos is null " + token);
- mPendingCapabilityRequests.remove(token);
- return;
- }
- logi("handleCapabilityReqResponse: token=" + token);
- mPresenceSubscriber.updatePresences(token, infos, true, null);
- }
-
- @Override
- public void updatePublisherState(@PresenceBase.PresencePublishState int publishState) {
- logi("updatePublisherState: from " + mPublishState + " to " + publishState);
- mPublishState = publishState;
- notifyPublishStateChanged(publishState);
- }
-
- private void addPublishRequestTaskId(int taskId) {
- synchronized (mRequestingPublishTaskIds) {
- mRequestingPublishTaskIds.add(taskId);
- }
- }
-
- private void removePublishRequestTaskId(int taskId) {
- synchronized (mRequestingPublishTaskIds) {
- mRequestingPublishTaskIds.remove(taskId);
- }
- }
-
- private boolean isPublishRequestExisted(Integer taskId) {
- synchronized (mRequestingPublishTaskIds) {
- return mRequestingPublishTaskIds.contains(taskId);
- }
- }
-
- private void addRequestingAvailabilityTaskId(int taskId) {
- synchronized (mPendingAvailabilityRequests) {
- mPendingAvailabilityRequests.contains(taskId);
- }
- }
-
- private void removeRequestingAvailabilityTaskId(int taskId) {
- synchronized (mPendingAvailabilityRequests) {
- mPendingAvailabilityRequests.remove(taskId);
- }
- }
-
- private boolean isAvailabilityRequestExisted(Integer taskId) {
- synchronized (mPendingAvailabilityRequests) {
- return mPendingAvailabilityRequests.contains(taskId);
- }
- }
-
- private boolean isCapabilityRequestExisted(Integer taskId) {
- return mPendingCapabilityRequests.containsKey(taskId);
- }
-
- private static String getNumberFromUri(Uri uri) {
- String number = uri.getSchemeSpecificPart();
- String[] numberParts = number.split("[@;:]");
-
- if (numberParts.length == 0) {
- return null;
- }
- return numberParts[0];
- }
-
- private static int toUcePublishState(int publishState) {
- switch (publishState) {
- case PresenceBase.PUBLISH_STATE_200_OK:
- return RcsUceAdapter.PUBLISH_STATE_OK;
- case PresenceBase.PUBLISH_STATE_NOT_PUBLISHED:
- return RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED;
- case PresenceBase.PUBLISH_STATE_VOLTE_PROVISION_ERROR:
- return RcsUceAdapter.PUBLISH_STATE_VOLTE_PROVISION_ERROR;
- case PresenceBase.PUBLISH_STATE_RCS_PROVISION_ERROR:
- return RcsUceAdapter.PUBLISH_STATE_RCS_PROVISION_ERROR;
- case PresenceBase.PUBLISH_STATE_REQUEST_TIMEOUT:
- return RcsUceAdapter.PUBLISH_STATE_REQUEST_TIMEOUT;
- case PresenceBase.PUBLISH_STATE_OTHER_ERROR:
- return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
- default:
- return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
- }
- }
-
- private static int toUceError(int resultCode) {
- switch (resultCode) {
- case ResultCode.SUBSCRIBE_NOT_REGISTERED:
- return RcsUceAdapter.ERROR_NOT_REGISTERED;
- case ResultCode.SUBSCRIBE_REQUEST_TIMEOUT:
- return RcsUceAdapter.ERROR_REQUEST_TIMEOUT;
- case ResultCode.SUBSCRIBE_FORBIDDEN:
- return RcsUceAdapter.ERROR_FORBIDDEN;
- case ResultCode.SUBSCRIBE_NOT_FOUND:
- return RcsUceAdapter.ERROR_NOT_FOUND;
- case ResultCode.SUBSCRIBE_TOO_LARGE:
- return RcsUceAdapter.ERROR_REQUEST_TOO_LARGE;
- case ResultCode.SUBSCRIBE_INSUFFICIENT_MEMORY:
- return RcsUceAdapter.ERROR_INSUFFICIENT_MEMORY;
- case ResultCode.SUBSCRIBE_LOST_NETWORK:
- return RcsUceAdapter.ERROR_LOST_NETWORK;
- case ResultCode.SUBSCRIBE_ALREADY_IN_QUEUE:
- return RcsUceAdapter.ERROR_ALREADY_IN_QUEUE;
- default:
- return RcsUceAdapter.ERROR_GENERIC_FAILURE;
- }
- }
-
- /*
- * Register receivers for updating capabilities
- */
- private void registerReceivers() {
- IntentFilter filter = new IntentFilter(TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
- filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- mContext.registerReceiver(mReceiver, filter);
-
- ContentResolver resolver = mContext.getContentResolver();
- if (resolver != null) {
- // Register mobile data content changed.
- resolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.MOBILE_DATA), false,
- mMobileDataObserver);
-
- // Register SIM info content changed.
- resolver.registerContentObserver(Telephony.SimInfo.CONTENT_URI, false,
- mSimInfoContentObserver);
- }
- }
-
- private void unregisterReceivers() {
- mContext.unregisterReceiver(mReceiver);
- ContentResolver resolver = mContext.getContentResolver();
- if (resolver != null) {
- resolver.unregisterContentObserver(mMobileDataObserver);
- resolver.unregisterContentObserver(mSimInfoContentObserver);
- }
- }
-
- /**
- * Register IMS and provision content changed.
- *
- * Call the UceImplHandler#registerImsContentChangedReceiver instead of
- * calling this method directly.
- */
- private void registerImsContentChangedReceiverInternal(int subId) {
- mUceImplHandler.removeRegisteringImsContentChangedReceiver();
- try {
- final int originalSubId = mSubId;
- if ((originalSubId == subId) && (mImsContentChangedCallbackRegistered)) {
- logi("registerImsContentChangedReceiverInternal: already registered. skip");
- return;
- }
- // Unregister original IMS and Provision callback
- unregisterImsProvisionCallback(originalSubId);
- // Register new IMS and Provision callback
- registerImsProvisionCallback(subId);
- } catch (ImsException e) {
- logw("registerImsContentChangedReceiverInternal error: " + e);
- mUceImplHandler.retryRegisteringImsContentChangedReceiver(subId);
- }
- }
-
- private void unregisterImsProvisionCallback(int subId) {
- if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- return;
- }
- // Unregister IMS callback
- ImsMmTelManager imsMmtelManager = getImsMmTelManager(subId);
- if (imsMmtelManager != null) {
- try {
- imsMmtelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback);
- imsMmtelManager.unregisterMmTelCapabilityCallback(mCapabilityCallback);
- } catch (RuntimeException e) {
- logw("unregister IMS callback error: " + e.getMessage());
- }
- }
-
- // Unregister provision changed callback
- ProvisioningManager provisioningManager =
- ProvisioningManager.createForSubscriptionId(subId);
- try {
- provisioningManager.unregisterProvisioningChangedCallback(mProvisioningChangedCallback);
- } catch (RuntimeException e) {
- logw("unregister provisioning callback error: " + e.getMessage());
- }
-
- // Remove all publish state callbacks
- clearPublishStateCallbacks();
-
- mImsContentChangedCallbackRegistered = false;
- }
-
- private void registerImsProvisionCallback(int subId) throws ImsException {
- if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- return;
- }
- // Register IMS callback
- ImsMmTelManager imsMmtelManager = getImsMmTelManager(subId);
- if (imsMmtelManager != null) {
- imsMmtelManager.registerImsRegistrationCallback(mContext.getMainExecutor(),
- mImsRegistrationCallback);
- imsMmtelManager.registerMmTelCapabilityCallback(mContext.getMainExecutor(),
- mCapabilityCallback);
- }
- // Register provision changed callback
- ProvisioningManager provisioningManager =
- ProvisioningManager.createForSubscriptionId(subId);
- provisioningManager.registerProvisioningChangedCallback(mContext.getMainExecutor(),
- mProvisioningChangedCallback);
-
- mImsContentChangedCallbackRegistered = true;
- logi("registerImsProvisionCallback");
- }
-
- private BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent == null) return;
- switch (intent.getAction()) {
- case TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED:
- int preferredMode = intent.getIntExtra(
- TelecomManager.EXTRA_TTY_PREFERRED_MODE, TelecomManager.TTY_MODE_OFF);
- logi("TTY preferred mode changed: " + preferredMode);
- mPresencePublication.onTtyPreferredModeChanged(preferredMode);
- break;
-
- case Intent.ACTION_AIRPLANE_MODE_CHANGED:
- boolean airplaneMode = intent.getBooleanExtra("state", false);
- logi("Airplane mode changed: " + airplaneMode);
- mPresencePublication.onAirplaneModeChanged(airplaneMode);
- break;
- }
- }
- };
-
- private ContentObserver mMobileDataObserver = new ContentObserver(
- new Handler(Looper.getMainLooper())) {
- @Override
- public void onChange(boolean selfChange) {
- boolean isEnabled = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.MOBILE_DATA, 1) == 1;
- logi("Mobile data changed: enabled=" + isEnabled);
- mPresencePublication.onMobileDataChanged(isEnabled);
- }
- };
-
- private ContentObserver mSimInfoContentObserver = new ContentObserver(
- new Handler(Looper.getMainLooper())) {
- @Override
- public void onChange(boolean selfChange) {
- if (mSubId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- return;
- }
-
- ImsMmTelManager ims = getImsMmTelManager(mSubId);
- if (ims == null) return;
-
- try {
- boolean isEnabled = ims.isVtSettingEnabled();
- logi("SimInfo changed: VT setting=" + isEnabled);
- mPresencePublication.onVtEnabled(isEnabled);
- } catch (RuntimeException e) {
- logw("SimInfo changed error: " + e);
- }
- }
- };
-
- private RegistrationManager.RegistrationCallback mImsRegistrationCallback =
- new RegistrationManager.RegistrationCallback() {
- @Override
- public void onRegistered(int imsTransportType) {
- logi("onRegistered: type=" + imsTransportType);
- mNetworkRegistrationType = imsTransportType;
- mPresencePublication.onImsConnected();
-
- // Also trigger PresencePublication#onFeatureCapabilityChanged method
- MmTelFeature.MmTelCapabilities capabilities = null;
- synchronized (mCapabilitiesLock) {
- capabilities = mMmTelCapabilities;
- }
-
- if (capabilities != null) {
- mPresencePublication.onFeatureCapabilityChanged(mNetworkRegistrationType,
- capabilities);
- }
- }
-
- @Override
- public void onUnregistered(ImsReasonInfo info) {
- logi("onUnregistered");
- mNetworkRegistrationType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
-
- // Also trigger PresencePublication#onFeatureCapabilityChanged method
- MmTelFeature.MmTelCapabilities capabilities = null;
- synchronized (mCapabilitiesLock) {
- capabilities = mMmTelCapabilities;
- }
-
- if (capabilities != null) {
- mPresencePublication.onFeatureCapabilityChanged(mNetworkRegistrationType,
- capabilities);
- }
- mPresencePublication.onImsDisconnected();
- }
- };
-
- private ImsMmTelManager.CapabilityCallback mCapabilityCallback =
- new ImsMmTelManager.CapabilityCallback() {
- @Override
- public void onCapabilitiesStatusChanged(MmTelFeature.MmTelCapabilities capabilities) {
- if (capabilities == null) {
- logw("onCapabilitiesStatusChanged: parameter is null");
- return;
- }
- synchronized (mCapabilitiesLock) {
- mMmTelCapabilities = capabilities;
- }
- mPresencePublication.onFeatureCapabilityChanged(mNetworkRegistrationType, capabilities);
- }
- };
-
- private ProvisioningManager.Callback mProvisioningChangedCallback =
- new ProvisioningManager.Callback() {
- @Override
- public void onProvisioningIntChanged(int item, int value) {
- logi("onProvisioningIntChanged: item=" + item);
- switch (item) {
- case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
- case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
- case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
- mPresencePublication.handleProvisioningChanged();
- break;
- default:
- break;
- }
- }
- };
-
- private boolean isCapabilityDiscoveryEnabled(int subId) {
- try {
- ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(subId);
- int discoveryEnabled = manager.getProvisioningIntValue(
- ProvisioningManager.KEY_RCS_CAPABILITY_DISCOVERY_ENABLED);
- return (discoveryEnabled == ProvisioningManager.PROVISIONING_VALUE_ENABLED);
- } catch (Exception e) {
- logw("isCapabilityDiscoveryEnabled error: " + e.getMessage());
- }
- return false;
- }
-
- private boolean isEabProvisioned(Context context, int subId) {
- if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- logw("isEabProvisioned error: invalid subscriptionId " + subId);
- return false;
- }
-
- CarrierConfigManager configManager = (CarrierConfigManager)
- context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager != null) {
- PersistableBundle config = configManager.getConfigForSubId(subId);
- if (config != null && !config.getBoolean(
- CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONED_BOOL)) {
- return true;
- }
- }
-
- try {
- ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(subId);
- int provisioningStatus = manager.getProvisioningIntValue(
- ProvisioningManager.KEY_EAB_PROVISIONING_STATUS);
- return (provisioningStatus == ProvisioningManager.PROVISIONING_VALUE_ENABLED);
- } catch (Exception e) {
- logw("isEabProvisioned error: " + e.getMessage());
- }
- return false;
- }
-
- private ImsMmTelManager getImsMmTelManager(int subId) {
- try {
- ImsManager imsManager = (ImsManager) mContext.getSystemService(
- Context.TELEPHONY_IMS_SERVICE);
- return (imsManager == null) ? null : imsManager.getImsMmTelManager(subId);
- } catch (IllegalArgumentException e) {
- logw("getImsMmTelManager error: " + e.getMessage());
- return null;
- }
- }
-
- private void logi(String log) {
- Log.i(LOG_TAG, getLogPrefix().append(log).toString());
- }
-
- private void logw(String log) {
- Log.w(LOG_TAG, getLogPrefix().append(log).toString());
- }
-
- private StringBuilder getLogPrefix() {
- StringBuilder builder = new StringBuilder("[");
- builder.append(mSlotId);
- builder.append("->");
- builder.append(mSubId);
- builder.append("] ");
- return builder;
- }
-}
diff --git a/testapps/ImsTestService/AndroidManifest.xml b/testapps/ImsTestService/AndroidManifest.xml
index 46d0721..6177e73 100644
--- a/testapps/ImsTestService/AndroidManifest.xml
+++ b/testapps/ImsTestService/AndroidManifest.xml
@@ -21,7 +21,7 @@
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<!--Beware, declaring the below permission will cause the device to not boot unless you add
this app and permission to frameworks/base/data/etc/privapp-permissions-platform.xml-->
- <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+ <!--uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/-->
<application android:label="ImsTestService"
android:directBootAware="true">
<activity android:name=".ImsTestServiceApp"
diff --git a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/SipTransportImpl.java b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/SipTransportImpl.java
new file mode 100644
index 0000000..1ae2594
--- /dev/null
+++ b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/SipTransportImpl.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 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 com.android.phone.testapps.imstestapp;
+
+import android.telephony.ims.stub.SipTransportImplBase;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Test stub implementation of SipTransport.
+ */
+public class SipTransportImpl extends SipTransportImplBase {
+
+ private static SipTransportImpl sSipTransportInstance;
+
+ public static SipTransportImpl getInstance(Executor e) {
+ if (sSipTransportInstance == null) {
+ sSipTransportInstance = new SipTransportImpl(e);
+ }
+ return sSipTransportInstance;
+ };
+
+ public SipTransportImpl(Executor e) {
+ super(e);
+ }
+}
diff --git a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestImsService.java b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestImsService.java
index 71323d8..477c638 100644
--- a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestImsService.java
+++ b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestImsService.java
@@ -39,6 +39,7 @@
public TestMmTelFeatureImpl mTestMmTelFeature;
public TestRcsFeatureImpl mTestRcsFeature;
public TestImsConfigImpl mTestImsConfig;
+ public SipTransportImpl mSipTransportImpl;
public static TestImsService getInstance() {
return mInstance;
@@ -51,7 +52,8 @@
mTestMmTelFeature = TestMmTelFeatureImpl.getInstance();
mTestRcsFeature = new TestRcsFeatureImpl();
mTestImsConfig = TestImsConfigImpl.getInstance();
-
+ mSipTransportImpl = SipTransportImpl.getInstance(
+ getApplicationContext().getMainExecutor());
mInstance = this;
}
@@ -60,10 +62,16 @@
return new ImsFeatureConfiguration.Builder()
.addFeature(0, ImsFeature.FEATURE_EMERGENCY_MMTEL)
.addFeature(0, ImsFeature.FEATURE_MMTEL)
+ .addFeature(0, ImsFeature.FEATURE_RCS)
.build();
}
@Override
+ public long getImsServiceCapabilities() {
+ return CAPABILITY_SIP_DELEGATE_CREATION;
+ }
+
+ @Override
public MmTelFeature createMmTelFeature(int slotId) {
Log.i(LOG_TAG, "TestImsService: onCreateMmTelImsFeature");
return mTestMmTelFeature;
@@ -84,4 +92,9 @@
public ImsConfigImplBase getConfig(int slotId) {
return mTestImsConfig;
}
+
+ @Override
+ public SipTransportImpl getSipTransport(int slotId) {
+ return mSipTransportImpl;
+ }
}
diff --git a/tests/src/com/android/TelephonyTestBase.java b/tests/src/com/android/TelephonyTestBase.java
index 132d893..502740d 100644
--- a/tests/src/com/android/TelephonyTestBase.java
+++ b/tests/src/com/android/TelephonyTestBase.java
@@ -27,6 +27,7 @@
import org.mockito.MockitoAnnotations;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
/**
@@ -58,6 +59,23 @@
PhoneConfigurationManager.unregisterAllMultiSimConfigChangeRegistrants();
}
+ protected final boolean waitForExecutorAction(Executor executor, long timeoutMillis) {
+ final CountDownLatch lock = new CountDownLatch(1);
+ Log.i("BRAD", "waitForExecutorAction");
+ executor.execute(() -> {
+ Log.i("BRAD", "countdown");
+ lock.countDown();
+ });
+ while (lock.getCount() > 0) {
+ try {
+ return lock.await(timeoutMillis, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ // do nothing
+ }
+ }
+ return true;
+ }
+
protected final void waitForHandlerAction(Handler h, long timeoutMillis) {
final CountDownLatch lock = new CountDownLatch(1);
h.post(lock::countDown);
diff --git a/tests/src/com/android/TestContext.java b/tests/src/com/android/TestContext.java
index 13bfe3b..9d712d3 100644
--- a/tests/src/com/android/TestContext.java
+++ b/tests/src/com/android/TestContext.java
@@ -17,7 +17,7 @@
package com.android;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doAnswer;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -32,9 +32,11 @@
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsManager;
import android.test.mock.MockContext;
+import android.util.SparseArray;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
import java.util.concurrent.Executor;
@@ -46,11 +48,19 @@
@Mock SubscriptionManager mMockSubscriptionManager;
@Mock ImsManager mMockImsManager;
- private PersistableBundle mCarrierConfig = new PersistableBundle();
+ private SparseArray<PersistableBundle> mCarrierConfigs = new SparseArray<>();
public TestContext() {
MockitoAnnotations.initMocks(this);
- doReturn(mCarrierConfig).when(mMockCarrierConfigManager).getConfigForSubId(anyInt());
+ doAnswer((Answer<PersistableBundle>) invocation -> {
+ int subId = (int) invocation.getArguments()[0];
+ if (subId < 0) {
+ return new PersistableBundle();
+ }
+ PersistableBundle b = mCarrierConfigs.get(subId);
+
+ return (b != null ? b : new PersistableBundle());
+ }).when(mMockCarrierConfigManager).getConfigForSubId(anyInt());
}
@Override
@@ -140,7 +150,15 @@
return null;
}
- public PersistableBundle getCarrierConfig() {
- return mCarrierConfig;
+ /**
+ * @return CarrierConfig PersistableBundle for the subscription specified.
+ */
+ public PersistableBundle getCarrierConfig(int subId) {
+ PersistableBundle b = mCarrierConfigs.get(subId);
+ if (b == null) {
+ b = new PersistableBundle();
+ mCarrierConfigs.put(subId, b);
+ }
+ return b;
}
}
diff --git a/tests/src/com/android/TestExecutorService.java b/tests/src/com/android/TestExecutorService.java
new file mode 100644
index 0000000..fec502a
--- /dev/null
+++ b/tests/src/com/android/TestExecutorService.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2020 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 com.android;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Delayed;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * An implementation of ExecutorService that just runs the requested task on the thread that it
+ * was called on for testing purposes.
+ */
+public class TestExecutorService implements ScheduledExecutorService {
+
+ private static class CompletedFuture<T> implements Future<T>, ScheduledFuture<T> {
+
+ private final Callable<T> mTask;
+ private final long mDelayMs;
+
+ CompletedFuture(Callable<T> task) {
+ mTask = task;
+ mDelayMs = 0;
+ }
+
+ CompletedFuture(Callable<T> task, long delayMs) {
+ mTask = task;
+ mDelayMs = delayMs;
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return false;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return false;
+ }
+
+ @Override
+ public boolean isDone() {
+ return true;
+ }
+
+ @Override
+ public T get() throws InterruptedException, ExecutionException {
+ try {
+ return mTask.call();
+ } catch (Exception e) {
+ throw new ExecutionException(e);
+ }
+ }
+
+ @Override
+ public T get(long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ try {
+ return mTask.call();
+ } catch (Exception e) {
+ throw new ExecutionException(e);
+ }
+ }
+
+ @Override
+ public long getDelay(TimeUnit unit) {
+ if (unit == TimeUnit.MILLISECONDS) {
+ return mDelayMs;
+ } else {
+ // not implemented
+ return 0;
+ }
+ }
+
+ @Override
+ public int compareTo(Delayed o) {
+ if (o == null) return 1;
+ if (o.getDelay(TimeUnit.MILLISECONDS) > mDelayMs) return -1;
+ if (o.getDelay(TimeUnit.MILLISECONDS) < mDelayMs) return 1;
+ return 0;
+ }
+ }
+
+ @Override
+ public void shutdown() {
+ }
+
+ @Override
+ public List<Runnable> shutdownNow() {
+ return null;
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return false;
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return false;
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) {
+ return false;
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ return new CompletedFuture<>(task);
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ task.run();
+ return new CompletedFuture<>(() -> null);
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout,
+ TimeUnit unit) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
+ // No need to worry about delays yet
+ command.run();
+ return new CompletedFuture<>(() -> null, delay);
+ }
+
+ @Override
+ public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+ return new CompletedFuture<>(callable, delay);
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period,
+ TimeUnit unit) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
+ long delay, TimeUnit unit) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ command.run();
+ }
+}
diff --git a/tests/src/com/android/services/telephony/ImsConferenceTest.java b/tests/src/com/android/services/telephony/ImsConferenceTest.java
index f13d709..7e6488d 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceTest.java
@@ -16,6 +16,8 @@
package com.android.services.telephony;
+import static junit.framework.Assert.assertTrue;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -573,4 +575,40 @@
assertEquals(0, imsConference.getNumberOfParticipants());
verify(mConferenceHost.mMockCall).hangup();
}
+
+ /**
+ * Verifies that an ImsConference can handle SIP and TEL URIs for both the P-Associated-Uri and
+ * conference event package identities.
+ */
+ @Test
+ public void testIsParticipantHost() {
+ // Simplest case, assume P-Associated-Uri is a tel URI and that the CEP participant is also
+ // a tel URI.
+ assertTrue(ImsConference.isParticipantHost(new Uri[] {
+ Uri.parse("tel:+8616505551234")},
+ Uri.parse("tel:+8616505551234")));
+
+ // Assume P-Associated-Uri is a tel URI and the CEP participant is a sip URI.
+ assertTrue(ImsConference.isParticipantHost(new Uri[] {
+ Uri.parse("tel:+8616505551234")},
+ Uri.parse("sip:+8616505551234@bj.ims.mnc011.mcc460.3gppnetwork.org")));
+
+ // Assume P-Associated-Uri is a sip URI and the CEP participant is a tel URI.
+ assertTrue(ImsConference.isParticipantHost(new Uri[] {
+ Uri.parse("sip:+8616505551234@bj.ims.mnc011.mcc460.3gppnetwork.org")},
+ Uri.parse("tel:+8616505551234")));
+
+ // Assume both P-Associated-Uri and the CEP participant are SIP URIs.
+ assertTrue(ImsConference.isParticipantHost(new Uri[] {
+ Uri.parse("sip:+8616505551234@bj.ims.mnc011.mcc460.3gppnetwork.org")},
+ Uri.parse("sip:+8616505551234@bj.ims.mnc011.mcc460.3gppnetwork.org")));
+
+ // Corner cases
+ assertFalse(ImsConference.isParticipantHost(new Uri[] {
+ Uri.parse("tel:+8616505551234")}, Uri.fromParts("", "", "")));
+ assertFalse(ImsConference.isParticipantHost(new Uri[] {
+ Uri.parse("tel:+8616505551234")}, null));
+ assertFalse(ImsConference.isParticipantHost(null, null));
+ assertFalse(ImsConference.isParticipantHost(new Uri[0], null));
+ }
}
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 2060e6f..07fe6a8 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -936,14 +936,14 @@
// Setup test to not support SUPL on the non-DDS subscription
doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
- getTestContext().getCarrierConfig().putStringArray(
+ getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
null);
testPhone.getServiceState().setRoaming(false);
- getTestContext().getCarrierConfig().putInt(
+ getTestContext().getCarrierConfig(0 /*subId*/).putInt(
CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
- getTestContext().getCarrierConfig().putString(
+ getTestContext().getCarrierConfig(0 /*subId*/).putString(
CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "150");
delayDialRunnable.run();
@@ -1021,14 +1021,14 @@
// Setup test to not support SUPL on the non-DDS subscription
doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
- getTestContext().getCarrierConfig().putStringArray(
+ getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
null);
testPhone.getServiceState().setRoaming(false);
- getTestContext().getCarrierConfig().putInt(
+ getTestContext().getCarrierConfig(0 /*subId*/).putInt(
CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
- getTestContext().getCarrierConfig().putString(
+ getTestContext().getCarrierConfig(0 /*subId*/).putString(
CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
delayDialRunnable.run();
@@ -1047,14 +1047,14 @@
// If the non-DDS supports SUPL, dont switch data
doReturn(false).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
- getTestContext().getCarrierConfig().putStringArray(
+ getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
null);
testPhone.getServiceState().setRoaming(false);
- getTestContext().getCarrierConfig().putInt(
+ getTestContext().getCarrierConfig(0 /*subId*/).putInt(
CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
- getTestContext().getCarrierConfig().putString(
+ getTestContext().getCarrierConfig(0 /*subId*/).putString(
CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
delayDialRunnable.run();
@@ -1073,14 +1073,14 @@
// Setup test to not support SUPL on the non-DDS subscription
doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
- getTestContext().getCarrierConfig().putStringArray(
+ getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
null);
testPhone.getServiceState().setRoaming(true);
- getTestContext().getCarrierConfig().putInt(
+ getTestContext().getCarrierConfig(0 /*subId*/).putInt(
CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
- getTestContext().getCarrierConfig().putString(
+ getTestContext().getCarrierConfig(0 /*subId*/).putString(
CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
delayDialRunnable.run();
@@ -1107,13 +1107,13 @@
doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
String[] roamingPlmns = new String[1];
roamingPlmns[0] = testRoamingOperator;
- getTestContext().getCarrierConfig().putStringArray(
+ getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
roamingPlmns);
- getTestContext().getCarrierConfig().putInt(
+ getTestContext().getCarrierConfig(0 /*subId*/).putInt(
CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
- getTestContext().getCarrierConfig().putString(
+ getTestContext().getCarrierConfig(0 /*subId*/).putString(
CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
delayDialRunnable.run();
@@ -1140,13 +1140,13 @@
doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
String[] roamingPlmns = new String[1];
roamingPlmns[0] = testRoamingOperator;
- getTestContext().getCarrierConfig().putStringArray(
+ getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
roamingPlmns);
- getTestContext().getCarrierConfig().putInt(
+ getTestContext().getCarrierConfig(0 /*subId*/).putInt(
CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
- getTestContext().getCarrierConfig().putString(
+ getTestContext().getCarrierConfig(0 /*subId*/).putString(
CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
delayDialRunnable.run();
diff --git a/tests/src/com/android/services/telephony/TelephonyManagerTest.java b/tests/src/com/android/services/telephony/TelephonyManagerTest.java
new file mode 100644
index 0000000..e9cdc98
--- /dev/null
+++ b/tests/src/com/android/services/telephony/TelephonyManagerTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 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 com.android.services.telephony;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
+import android.test.mock.MockContext;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.telephony.ITelephony;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Unit tests for {@link TelephonyManager}. */
+@RunWith(AndroidJUnit4.class)
+public class TelephonyManagerTest {
+ private static final String PKG_NAME = "Unittest.TelephonyManagerTest";
+ private static final String TAG = "TelephonyManagerTest";
+
+ private ITelephony mMockITelephony;
+ private SubscriptionManager mMockSubscriptionManager;
+ private Context mMockContext;
+
+ private TelephonyManager mTelephonyManager;
+
+ private final MockContext mContext =
+ new MockContext() {
+ @Override
+ public String getOpPackageName() {
+ return PKG_NAME;
+ }
+ @Override
+ public String getAttributionTag() {
+ return TAG;
+ }
+ @Override
+ public Context getApplicationContext() {
+ return null;
+ }
+ @Override
+ public Object getSystemService(String name) {
+ switch (name) {
+ case (Context.TELEPHONY_SUBSCRIPTION_SERVICE) : {
+ return mMockSubscriptionManager;
+ }
+ }
+ return null;
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ mMockITelephony = mock(ITelephony.class);
+ mMockSubscriptionManager = mock(SubscriptionManager.class);
+ mMockContext = mock(Context.class);
+ when(mMockContext.getSystemService(eq(Context.TELEPHONY_SUBSCRIPTION_SERVICE)))
+ .thenReturn(mMockSubscriptionManager);
+
+ mTelephonyManager = new TelephonyManager(mContext);
+ TelephonyManager.setupITelephonyForTest(mMockITelephony);
+ TelephonyManager.enableServiceHandleCaching();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ TelephonyManager.setupITelephonyForTest(null);
+ TelephonyManager.disableServiceHandleCaching();
+ }
+
+ @Test
+ public void testGetEmergencyNumberListForCategories() throws Exception {
+ Map<Integer, List<EmergencyNumber>> emergencyNumberLists = new HashMap<>();
+ List<EmergencyNumber> emergencyNumberList = new ArrayList<>();
+ EmergencyNumber number_police = new EmergencyNumber(
+ "911",
+ "us",
+ "30",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE,
+ new ArrayList<String>(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+ EmergencyNumber number_fire = new EmergencyNumber(
+ "912",
+ "us",
+ "30",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
+ new ArrayList<String>(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+ emergencyNumberList.add(number_police);
+ emergencyNumberList.add(number_fire);
+ final int test_sub_id = 1;
+ emergencyNumberLists.put(test_sub_id, emergencyNumberList);
+ when(mMockITelephony.getEmergencyNumberList(eq(PKG_NAME), eq(TAG))).thenReturn(
+ emergencyNumberLists);
+
+ // Call TelephonyManager.getEmergencyNumberList(Category)
+ Map<Integer, List<EmergencyNumber>> returnedEmergencyNumberLists =
+ mTelephonyManager.getEmergencyNumberList(
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE);
+
+ // Verify the ITelephony service is called
+ verify(mMockITelephony, times(1)).getEmergencyNumberList(eq(PKG_NAME), eq(TAG));
+
+ // Verify the returned number list contains only the police number(s)
+ List<EmergencyNumber> returnedEmergencyNumberList = returnedEmergencyNumberLists.get(
+ test_sub_id);
+ for (EmergencyNumber num : returnedEmergencyNumberList) {
+ assertTrue(num.isInEmergencyServiceCategories(
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE));
+ }
+ }
+}
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index 09cec17..67e0329 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -21,6 +21,7 @@
import android.os.Bundle;
import android.os.PersistableBundle;
import android.telecom.PhoneAccountHandle;
+import android.telephony.TelephonyManager;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -61,6 +62,9 @@
Resources mMockResources;
@Mock
+ TelephonyManager mMockTelephonyManager;
+
+ @Mock
EmergencyNumberTracker mEmergencyNumberTracker;
private Phone mMockPhone;
@@ -84,6 +88,7 @@
mMockPhone = mock(Phone.class);
mMockContext = mock(Context.class);
+ mMockTelephonyManager = mock(TelephonyManager.class);
mOriginalConnection = mMockRadioConnection;
// Set up mMockRadioConnection and mMockPhone to contain an active call
when(mMockRadioConnection.getState()).thenReturn(Call.State.ACTIVE);
@@ -101,6 +106,8 @@
when(mMockPhone.getContext()).thenReturn(mMockContext);
when(mMockPhone.getCurrentSubscriberUris()).thenReturn(null);
when(mMockContext.getResources()).thenReturn(mMockResources);
+ when(mMockContext.getSystemService(Context.TELEPHONY_SERVICE))
+ .thenReturn(mMockTelephonyManager);
when(mMockResources.getBoolean(anyInt())).thenReturn(false);
when(mMockPhone.getDefaultPhone()).thenReturn(mMockPhone);
when(mMockPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_IMS);
@@ -169,6 +176,12 @@
// Requires ImsManager dependencies, do not implement during testing.
}
+ @Override
+ boolean isWfcEnabled(Phone phone) {
+ // Requires ImsManager dependencies, mock for test.
+ return true;
+ }
+
public int getNotifyPhoneAccountChangedCount() {
return mNotifyPhoneAccountChangedCount;
}
diff --git a/tests/src/com/android/services/telephony/rcs/DelegateStateTrackerTest.java b/tests/src/com/android/services/telephony/rcs/DelegateStateTrackerTest.java
new file mode 100644
index 0000000..4d40702
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/DelegateStateTrackerTest.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2020 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 com.android.services.telephony.rcs;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateConnectionStateCallback;
+import android.util.ArraySet;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class DelegateStateTrackerTest extends TelephonyTestBase {
+ private static final int TEST_SUB_ID = 1;
+
+ @Mock private ISipDelegate mSipDelegate;
+ @Mock private ISipDelegateConnectionStateCallback mAppCallback;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * When an underlying SipDelegate is created, the app should only receive one onCreated callback
+ * independent of how many times sipDelegateConnected is called. Once created, registration
+ * and IMS configuration events should propagate up to the app as well.
+ */
+ @SmallTest
+ @Test
+ public void testDelegateCreated() throws Exception {
+ DelegateStateTracker stateTracker = new DelegateStateTracker(TEST_SUB_ID, mAppCallback,
+ mSipDelegate);
+ Set<FeatureTagState> deniedTags = getMmTelDeniedTag();
+ stateTracker.sipDelegateConnected(deniedTags);
+ // Calling connected multiple times should not generate multiple onCreated events.
+ stateTracker.sipDelegateConnected(deniedTags);
+ verify(mAppCallback).onCreated(mSipDelegate);
+
+ // Ensure status updates are sent to app as expected.
+ DelegateRegistrationState regState = new DelegateRegistrationState.Builder()
+ .addRegisteredFeatureTag(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG)
+ .build();
+ SipDelegateImsConfiguration config = new SipDelegateImsConfiguration.Builder(1/*version*/)
+ .build();
+ stateTracker.onRegistrationStateChanged(regState);
+ stateTracker.onImsConfigurationChanged(config);
+ verify(mAppCallback).onFeatureTagStatusChanged(eq(regState),
+ eq(new ArrayList<>(deniedTags)));
+ verify(mAppCallback).onImsConfigurationChanged(config);
+
+ verify(mAppCallback, never()).onDestroyed(anyInt());
+ }
+
+ /**
+ * onDestroyed should be called when sipDelegateDestroyed is called.
+ */
+ @SmallTest
+ @Test
+ public void testDelegateDestroyed() throws Exception {
+ DelegateStateTracker stateTracker = new DelegateStateTracker(TEST_SUB_ID, mAppCallback,
+ mSipDelegate);
+ Set<FeatureTagState> deniedTags = getMmTelDeniedTag();
+ stateTracker.sipDelegateConnected(deniedTags);
+
+ stateTracker.sipDelegateDestroyed(
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ verify(mAppCallback).onDestroyed(
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ }
+
+ /**
+ * When a SipDelegate is created and then an event occurs that will destroy->create a new
+ * SipDelegate underneath, we need to move the state of the features that are reporting
+ * registered to DEREGISTERING_REASON_FEATURE_TAGS_CHANGING so that the app can close dialogs on
+ * it. Once the new underlying SipDelegate is created, we must verify that the new registration
+ * is propagated up without any overrides.
+ */
+ @SmallTest
+ @Test
+ public void testDelegateChangingRegisteredTagsOverride() throws Exception {
+ DelegateStateTracker stateTracker = new DelegateStateTracker(TEST_SUB_ID, mAppCallback,
+ mSipDelegate);
+ Set<FeatureTagState> deniedTags = getMmTelDeniedTag();
+ stateTracker.sipDelegateConnected(deniedTags);
+ // SipDelegate created
+ verify(mAppCallback).onCreated(mSipDelegate);
+ DelegateRegistrationState regState = new DelegateRegistrationState.Builder()
+ .addRegisteredFeatureTag(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG)
+ .addDeregisteringFeatureTag(ImsSignallingUtils.FILE_TRANSFER_HTTP_TAG,
+ DelegateRegistrationState.DEREGISTERING_REASON_PROVISIONING_CHANGE)
+ .addDeregisteredFeatureTag(ImsSignallingUtils.GROUP_CHAT_TAG,
+ DelegateRegistrationState.DEREGISTERED_REASON_NOT_PROVISIONED)
+ .build();
+ stateTracker.onRegistrationStateChanged(regState);
+ // Simulate underlying SipDelegate switch
+ stateTracker.sipDelegateChanging(
+ DelegateRegistrationState.DEREGISTERING_REASON_FEATURE_TAGS_CHANGING);
+ // onFeatureTagStatusChanged should now be called with registered features overridden with
+ // DEREGISTERING_REASON_FEATURE_TAGS_CHANGING
+ DelegateRegistrationState overrideRegState = new DelegateRegistrationState.Builder()
+ .addDeregisteringFeatureTag(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG,
+ DelegateRegistrationState.DEREGISTERING_REASON_FEATURE_TAGS_CHANGING)
+ // Already Deregistering/Deregistered tags should not be overridden.
+ .addDeregisteringFeatureTag(ImsSignallingUtils.FILE_TRANSFER_HTTP_TAG,
+ DelegateRegistrationState.DEREGISTERING_REASON_PROVISIONING_CHANGE)
+ .addDeregisteredFeatureTag(ImsSignallingUtils.GROUP_CHAT_TAG,
+ DelegateRegistrationState.DEREGISTERED_REASON_NOT_PROVISIONED)
+ .build();
+ // new underlying SipDelegate created
+ stateTracker.sipDelegateConnected(deniedTags);
+ stateTracker.onRegistrationStateChanged(regState);
+
+ // Verify registration state through the process:
+ ArgumentCaptor<DelegateRegistrationState> regCaptor =
+ ArgumentCaptor.forClass(DelegateRegistrationState.class);
+ verify(mAppCallback, times(3)).onFeatureTagStatusChanged(
+ regCaptor.capture(), eq(new ArrayList<>(deniedTags)));
+ List<DelegateRegistrationState> testStates = regCaptor.getAllValues();
+ // feature tags should first be registered
+ assertEquals(regState, testStates.get(0));
+ // registered feature tags should have moved to deregistering
+ assertEquals(overrideRegState, testStates.get(1));
+ // and then moved back to registered after underlying FT change done.
+ assertEquals(regState, testStates.get(2));
+
+ //onCreate should only have been called once and onDestroy should have never been called.
+ verify(mAppCallback).onCreated(mSipDelegate);
+ verify(mAppCallback, never()).onDestroyed(anyInt());
+ }
+
+ /**
+ * Test the case that when the underlying Denied tags change in the SipDelegate, the change is
+ * properly shown in the registration update event.
+ */
+ @SmallTest
+ @Test
+ public void testDelegateChangingDeniedTagsChanged() throws Exception {
+ DelegateStateTracker stateTracker = new DelegateStateTracker(TEST_SUB_ID, mAppCallback,
+ mSipDelegate);
+ Set<FeatureTagState> deniedTags = getMmTelDeniedTag();
+ stateTracker.sipDelegateConnected(deniedTags);
+ // SipDelegate created
+ verify(mAppCallback).onCreated(mSipDelegate);
+ DelegateRegistrationState regState = new DelegateRegistrationState.Builder()
+ .addRegisteredFeatureTag(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG)
+ .build();
+ stateTracker.onRegistrationStateChanged(regState);
+ // Simulate underlying SipDelegate switch
+ stateTracker.sipDelegateChanging(
+ DelegateRegistrationState.DEREGISTERING_REASON_FEATURE_TAGS_CHANGING);
+ // onFeatureTagStatusChanged should now be called with registered features overridden with
+ // DEREGISTERING_REASON_FEATURE_TAGS_CHANGING
+ DelegateRegistrationState overrideRegState = new DelegateRegistrationState.Builder()
+ .addDeregisteringFeatureTag(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG,
+ DelegateRegistrationState.DEREGISTERING_REASON_FEATURE_TAGS_CHANGING)
+ .build();
+ // Verify registration state so far.
+ ArgumentCaptor<DelegateRegistrationState> regCaptor =
+ ArgumentCaptor.forClass(DelegateRegistrationState.class);
+ verify(mAppCallback, times(2)).onFeatureTagStatusChanged(
+ regCaptor.capture(), eq(new ArrayList<>(deniedTags)));
+ List<DelegateRegistrationState> testStates = regCaptor.getAllValues();
+ assertEquals(2, testStates.size());
+ // feature tags should first be registered
+ assertEquals(regState, testStates.get(0));
+ // registered feature tags should have moved to deregistering
+ assertEquals(overrideRegState, testStates.get(1));
+
+ // new underlying SipDelegate created, but SipDelegate denied one to one chat
+ deniedTags.add(new FeatureTagState(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG,
+ SipDelegateManager.DENIED_REASON_NOT_ALLOWED));
+ stateTracker.sipDelegateConnected(deniedTags);
+ DelegateRegistrationState fullyDeniedRegState = new DelegateRegistrationState.Builder()
+ .build();
+ // In this special case, it will be the SipDelegateConnectionBase that will trigger
+ // reg state change.
+ stateTracker.onRegistrationStateChanged(fullyDeniedRegState);
+ verify(mAppCallback).onFeatureTagStatusChanged(regCaptor.capture(),
+ eq(new ArrayList<>(deniedTags)));
+ // now all feature tags denied, so we should see only denied tags.
+ assertEquals(fullyDeniedRegState, regCaptor.getValue());
+
+ //onCreate should only have been called once and onDestroy should have never been called.
+ verify(mAppCallback).onCreated(mSipDelegate);
+ verify(mAppCallback, never()).onDestroyed(anyInt());
+ }
+
+ /**
+ * Test that when we move from changing tags state to the delegate being destroyed, we get the
+ * correct onDestroy event sent to the app.
+ */
+ @SmallTest
+ @Test
+ public void testDelegateChangingDeniedTagsChangingToDestroy() throws Exception {
+ DelegateStateTracker stateTracker = new DelegateStateTracker(TEST_SUB_ID, mAppCallback,
+ mSipDelegate);
+ Set<FeatureTagState> deniedTags = getMmTelDeniedTag();
+ stateTracker.sipDelegateConnected(deniedTags);
+ // SipDelegate created
+ verify(mAppCallback).onCreated(mSipDelegate);
+ DelegateRegistrationState regState = new DelegateRegistrationState.Builder()
+ .addRegisteredFeatureTag(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG)
+ .addDeregisteredFeatureTag(ImsSignallingUtils.GROUP_CHAT_TAG,
+ DelegateRegistrationState.DEREGISTERED_REASON_NOT_PROVISIONED)
+ .build();
+ stateTracker.onRegistrationStateChanged(regState);
+ verify(mAppCallback).onFeatureTagStatusChanged(any(),
+ eq(new ArrayList<>(deniedTags)));
+ // Simulate underlying SipDelegate switch
+ stateTracker.sipDelegateChanging(
+ DelegateRegistrationState.DEREGISTERING_REASON_DESTROY_PENDING);
+ // Destroy
+ stateTracker.sipDelegateDestroyed(
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+
+ // onFeatureTagStatusChanged should now be called with registered features overridden with
+ // DEREGISTERING_REASON_DESTROY_PENDING
+ DelegateRegistrationState overrideRegState = new DelegateRegistrationState.Builder()
+ .addDeregisteringFeatureTag(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG,
+ DelegateRegistrationState.DEREGISTERING_REASON_DESTROY_PENDING)
+ // Deregistered should stay the same.
+ .addDeregisteredFeatureTag(ImsSignallingUtils.GROUP_CHAT_TAG,
+ DelegateRegistrationState.DEREGISTERED_REASON_NOT_PROVISIONED)
+ .build();
+ // Verify registration state through process:
+ ArgumentCaptor<DelegateRegistrationState> regCaptor =
+ ArgumentCaptor.forClass(DelegateRegistrationState.class);
+ verify(mAppCallback, times(2)).onFeatureTagStatusChanged(regCaptor.capture(),
+ eq(new ArrayList<>(deniedTags)));
+ List<DelegateRegistrationState> testStates = regCaptor.getAllValues();
+ assertEquals(2, testStates.size());
+ // feature tags should first be registered
+ assertEquals(regState, testStates.get(0));
+ // registered feature tags should have moved to deregistering
+ assertEquals(overrideRegState, testStates.get(1));
+ //onCreate/onDestroy should only be called once.
+ verify(mAppCallback).onCreated(mSipDelegate);
+ verify(mAppCallback).onDestroyed(
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ }
+
+ private Set<FeatureTagState> getMmTelDeniedTag() {
+ Set<FeatureTagState> deniedTags = new ArraySet<>();
+ deniedTags.add(new FeatureTagState(ImsSignallingUtils.MMTEL_TAG,
+ SipDelegateManager.DENIED_REASON_NOT_ALLOWED));
+ return deniedTags;
+ }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/ImsSignallingUtils.java b/tests/src/com/android/services/telephony/rcs/ImsSignallingUtils.java
new file mode 100644
index 0000000..d607f6d
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/ImsSignallingUtils.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 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 com.android.services.telephony.rcs;
+
+/**
+ * Various definitions and utilities related to IMS Signalling.
+ */
+public class ImsSignallingUtils {
+ public static final String MMTEL_TAG =
+ "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\"";
+ public static final String ONE_TO_ONE_CHAT_TAG =
+ "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gppservice.ims.icsi.oma.cpm.msg\"";
+ public static final String GROUP_CHAT_TAG =
+ "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gppservice.ims.icsi.oma.cpm.session\"";
+ public static final String FILE_TRANSFER_HTTP_TAG =
+ "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gppapplication.ims.iari.rcs.fthttp\"";
+}
diff --git a/tests/src/com/android/services/telephony/rcs/MessageTransportStateTrackerTest.java b/tests/src/com/android/services/telephony/rcs/MessageTransportStateTrackerTest.java
new file mode 100644
index 0000000..5e05085
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/MessageTransportStateTrackerTest.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2020 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 com.android.services.telephony.rcs;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.RemoteException;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+@RunWith(AndroidJUnit4.class)
+public class MessageTransportStateTrackerTest extends TelephonyTestBase {
+ private static final int TEST_SUB_ID = 1;
+
+ private static final SipMessage TEST_MESSAGE = new SipMessage(
+ "INVITE sip:callee@ex.domain.com SIP/2.0",
+ "Via: SIP/2.0/UDP ex.place.com;branch=z9hG4bK776asdhds",
+ new byte[0]);
+
+ // Use for finer-grained control of when the Executor executes.
+ private static class PendingExecutor implements Executor {
+ private final ArrayList<Runnable> mPendingRunnables = new ArrayList<>();
+
+ @Override
+ public void execute(Runnable command) {
+ mPendingRunnables.add(command);
+ }
+
+ public void executePending() {
+ for (Runnable r : mPendingRunnables) {
+ r.run();
+ }
+ mPendingRunnables.clear();
+ }
+ }
+
+ @Mock private ISipDelegateMessageCallback mDelegateMessageCallback;
+ @Mock private ISipDelegate mISipDelegate;
+ @Mock private Consumer<Boolean> mMockCloseConsumer;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @SmallTest
+ @Test
+ public void testDelegateConnectionSendOutgoingMessage() throws Exception {
+ MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
+ Runnable::run, mDelegateMessageCallback);
+
+ tracker.openTransport(mISipDelegate, Collections.emptySet());
+ tracker.getDelegateConnection().sendMessage(TEST_MESSAGE, 1 /*version*/);
+ verify(mISipDelegate).sendMessage(TEST_MESSAGE, 1 /*version*/);
+
+ doThrow(new RemoteException()).when(mISipDelegate).sendMessage(any(), anyLong());
+ tracker.getDelegateConnection().sendMessage(TEST_MESSAGE, 1 /*version*/);
+ verify(mDelegateMessageCallback).onMessageSendFailure(any(),
+ eq(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD));
+
+ tracker.close(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED);
+ tracker.getDelegateConnection().sendMessage(TEST_MESSAGE, 1 /*version*/);
+ verify(mDelegateMessageCallback).onMessageSendFailure(any(),
+ eq(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED));
+ }
+
+ @SmallTest
+ @Test
+ public void testDelegateConnectionCloseGracefully() throws Exception {
+ PendingExecutor executor = new PendingExecutor();
+ MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
+ executor, mDelegateMessageCallback);
+
+ tracker.openTransport(mISipDelegate, Collections.emptySet());
+ tracker.getDelegateConnection().sendMessage(TEST_MESSAGE, 1 /*version*/);
+ executor.executePending();
+ verify(mISipDelegate).sendMessage(TEST_MESSAGE, 1 /*version*/);
+ verify(mDelegateMessageCallback, never()).onMessageSendFailure(any(), anyInt());
+
+ // Use PendingExecutor a little weird here, we need to queue sendMessage first, even though
+ // closeGracefully will complete partly synchronously to test that the pending message will
+ // return MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION before the scheduled
+ // graceful close operation completes.
+ tracker.getDelegateConnection().sendMessage(TEST_MESSAGE, 1 /*version*/);
+ tracker.closeGracefully(
+ SipDelegateManager.MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION,
+ SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
+ mMockCloseConsumer);
+ verify(mMockCloseConsumer, never()).accept(any());
+ // resolve pending close operation
+ executor.executePending();
+ verify(mDelegateMessageCallback).onMessageSendFailure(any(),
+ eq(SipDelegateManager.MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION));
+ // Still should only report one call of sendMessage from before
+ verify(mISipDelegate).sendMessage(TEST_MESSAGE, 1 /*version*/);
+ verify(mMockCloseConsumer).accept(true);
+
+ // ensure that after close operation completes, we get the correct
+ // MESSAGE_FAILURE_REASON_DELEGATE_CLOSED message.
+ tracker.getDelegateConnection().sendMessage(TEST_MESSAGE, 1 /*version*/);
+ executor.executePending();
+ verify(mDelegateMessageCallback).onMessageSendFailure(any(),
+ eq(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED));
+ // Still should only report one call of sendMessage from before
+ verify(mISipDelegate).sendMessage(TEST_MESSAGE, 1 /*version*/);
+ }
+
+ @SmallTest
+ @Test
+ public void testDelegateConnectionNotifyMessageReceived() throws Exception {
+ MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
+ Runnable::run, mDelegateMessageCallback);
+ tracker.openTransport(mISipDelegate, Collections.emptySet());
+ tracker.getDelegateConnection().notifyMessageReceived("z9hG4bK776asdhds");
+ verify(mISipDelegate).notifyMessageReceived("z9hG4bK776asdhds");
+ }
+
+ @SmallTest
+ @Test
+ public void testDelegateConnectionNotifyMessageReceiveError() throws Exception {
+ MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
+ Runnable::run, mDelegateMessageCallback);
+ tracker.openTransport(mISipDelegate, Collections.emptySet());
+ tracker.getDelegateConnection().notifyMessageReceiveError("z9hG4bK776asdhds",
+ SipDelegateManager.MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE);
+ verify(mISipDelegate).notifyMessageReceiveError("z9hG4bK776asdhds",
+ SipDelegateManager.MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE);
+ }
+
+ @SmallTest
+ @Test
+ public void testDelegateConnectionCloseDialog() throws Exception {
+ MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
+ Runnable::run, mDelegateMessageCallback);
+ tracker.openTransport(mISipDelegate, Collections.emptySet());
+ tracker.getDelegateConnection().closeDialog("testCallId");
+ verify(mISipDelegate).closeDialog("testCallId");
+ }
+
+ @SmallTest
+ @Test
+ public void testDelegateOnMessageReceived() throws Exception {
+ MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
+ Runnable::run, mDelegateMessageCallback);
+ tracker.openTransport(mISipDelegate, Collections.emptySet());
+
+ tracker.getMessageCallback().onMessageReceived(TEST_MESSAGE);
+ verify(mDelegateMessageCallback).onMessageReceived(TEST_MESSAGE);
+
+ doThrow(new RemoteException()).when(mDelegateMessageCallback).onMessageReceived(any());
+ tracker.getMessageCallback().onMessageReceived(TEST_MESSAGE);
+ verify(mISipDelegate).notifyMessageReceiveError(any(),
+ eq(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD));
+ }
+
+ @SmallTest
+ @Test
+ public void testDelegateOnMessageReceivedClosedGracefully() throws Exception {
+ PendingExecutor executor = new PendingExecutor();
+ MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
+ executor, mDelegateMessageCallback);
+ tracker.openTransport(mISipDelegate, Collections.emptySet());
+
+ tracker.getMessageCallback().onMessageReceived(TEST_MESSAGE);
+ executor.executePending();
+ verify(mDelegateMessageCallback).onMessageReceived(TEST_MESSAGE);
+
+ tracker.getMessageCallback().onMessageReceived(TEST_MESSAGE);
+ tracker.closeGracefully(
+ SipDelegateManager.MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION,
+ SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
+ mMockCloseConsumer);
+ executor.executePending();
+ // Incoming SIP message should not be blocked by closeGracefully
+ verify(mDelegateMessageCallback, times(2)).onMessageReceived(TEST_MESSAGE);
+ }
+
+ @SmallTest
+ @Test
+ public void testDelegateOnMessageSent() throws Exception {
+ MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
+ Runnable::run, mDelegateMessageCallback);
+ tracker.openTransport(mISipDelegate, Collections.emptySet());
+ tracker.getMessageCallback().onMessageSent("z9hG4bK776asdhds");
+ verify(mDelegateMessageCallback).onMessageSent("z9hG4bK776asdhds");
+ }
+
+ @SmallTest
+ @Test
+ public void testDelegateonMessageSendFailure() throws Exception {
+ MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
+ Runnable::run, mDelegateMessageCallback);
+ tracker.openTransport(mISipDelegate, Collections.emptySet());
+ tracker.getMessageCallback().onMessageSendFailure("z9hG4bK776asdhds",
+ SipDelegateManager.MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE);
+ verify(mDelegateMessageCallback).onMessageSendFailure("z9hG4bK776asdhds",
+ SipDelegateManager.MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE);
+ }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
index fbb270d..7e87dc7 100644
--- a/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
@@ -100,7 +100,8 @@
verify(mMockFeature).onRcsConnected(mFeatureManager);
// Disconnect
- mConnectorListener.getValue().connectionUnavailable();
+ mConnectorListener.getValue().connectionUnavailable(
+ FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);
verify(mFeatureManager).unregisterImsRegistrationCallback(any());
verify(mMockFeature, times(2)).onRcsDisconnected();
@@ -193,7 +194,8 @@
public void testFeatureManagerDisconnectedAddFeature() {
RcsFeatureController controller = createFeatureController();
// Disconnect the RcsFeatureManager
- mConnectorListener.getValue().connectionUnavailable();
+ mConnectorListener.getValue().connectionUnavailable(
+ FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);
controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
verify(mMockFeature).onRcsDisconnected();
@@ -205,7 +207,8 @@
IImsRegistrationCallback regCb = mock(IImsRegistrationCallback.class);
IImsCapabilityCallback capCb = mock(IImsCapabilityCallback.class);
// Disconnect the RcsFeatureManager
- mConnectorListener.getValue().connectionUnavailable();
+ mConnectorListener.getValue().connectionUnavailable(
+ FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);
try {
controller.registerImsRegistrationCallback(0 /*subId*/, null /*callback*/);
diff --git a/tests/src/com/android/services/telephony/rcs/SipDelegateBinderConnectionTest.java b/tests/src/com/android/services/telephony/rcs/SipDelegateBinderConnectionTest.java
new file mode 100644
index 0000000..fa439dc
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/SipDelegateBinderConnectionTest.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2020 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 com.android.services.telephony.rcs;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.verify;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.aidl.ISipDelegateStateCallback;
+import android.telephony.ims.aidl.ISipTransport;
+import android.util.ArraySet;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+@RunWith(AndroidJUnit4.class)
+public class SipDelegateBinderConnectionTest extends TelephonyTestBase {
+ private static final int TEST_SUB_ID = 1;
+
+ @Mock private ISipDelegate mMockDelegate;
+ @Mock private ISipTransport mMockTransport;
+ @Mock private IBinder mTransportBinder;
+ @Mock private ISipDelegateMessageCallback mMessageCallback;
+ @Mock private DelegateBinderStateManager.StateCallback mMockStateCallback;
+ @Mock private BiConsumer<ISipDelegate, Set<FeatureTagState>> mMockCreatedCallback;
+ @Mock private Consumer<Integer> mMockDestroyedCallback;
+
+ private ArrayList<SipDelegateBinderConnection.StateCallback> mStateCallbackList;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ doReturn(mTransportBinder).when(mMockTransport).asBinder();
+ mStateCallbackList = new ArrayList<>(1);
+ mStateCallbackList.add(mMockStateCallback);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @SmallTest
+ @Test
+ public void testBaseImpl() throws Exception {
+ DelegateBinderStateManager baseConnection = new SipDelegateBinderConnectionStub(
+ getMmTelDeniedTag(), Runnable::run, mStateCallbackList);
+
+ baseConnection.create(null /*message cb*/, mMockCreatedCallback);
+ // Verify the stub simulates onCreated + on registration state callback.
+ verify(mMockCreatedCallback).accept(any(), eq(getMmTelDeniedTag()));
+ verify(mMockStateCallback).onRegistrationStateChanged(
+ new DelegateRegistrationState.Builder().build());
+
+ // Verify onDestroyed is called correctly.
+ baseConnection.destroy(SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP,
+ mMockDestroyedCallback);
+ verify(mMockDestroyedCallback).accept(
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ }
+
+ @SmallTest
+ @Test
+ public void testCreateConnection() throws Exception {
+ DelegateRequest request = getDelegateRequest();
+ ArraySet<FeatureTagState> deniedTags = getMmTelDeniedTag();
+ SipDelegateBinderConnection connection = new SipDelegateBinderConnection(TEST_SUB_ID,
+ mMockTransport, request, deniedTags, Runnable::run, mStateCallbackList);
+ ISipDelegateStateCallback cb = createDelegateCaptureStateCallback(request, connection);
+
+ // Send onCreated callback from SipDelegate
+ ArrayList<FeatureTagState> delegateDeniedTags = new ArrayList<>(1);
+ delegateDeniedTags.add(new FeatureTagState(ImsSignallingUtils.GROUP_CHAT_TAG,
+ SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE));
+ assertNotNull(cb);
+ cb.onCreated(mMockDelegate, delegateDeniedTags);
+
+ ArraySet<FeatureTagState> totalDeniedTags = new ArraySet<>(deniedTags);
+ // Add the tags denied by the controller as well.
+ totalDeniedTags.addAll(delegateDeniedTags);
+ // The callback should contain the controller and delegate denied tags in the callback.
+ verify(mMockCreatedCallback).accept(mMockDelegate, totalDeniedTags);
+ }
+
+ @SmallTest
+ @Test
+ public void testCreateConnectionServiceDead() throws Exception {
+ DelegateRequest request = getDelegateRequest();
+ ArraySet<FeatureTagState> deniedTags = getMmTelDeniedTag();
+ SipDelegateBinderConnection connection = new SipDelegateBinderConnection(TEST_SUB_ID,
+ mMockTransport, request, deniedTags, Runnable::run, mStateCallbackList);
+ doThrow(new RemoteException()).when(mMockTransport).createSipDelegate(eq(TEST_SUB_ID),
+ any(), any(), any());
+ ISipDelegateStateCallback cb = createDelegateCaptureStateCallback(request, connection);
+ assertNull(cb);
+ }
+
+ @SmallTest
+ @Test
+ public void testDestroyConnection() throws Exception {
+ DelegateRequest request = getDelegateRequest();
+ ArraySet<FeatureTagState> deniedTags = getMmTelDeniedTag();
+ SipDelegateBinderConnection connection = new SipDelegateBinderConnection(TEST_SUB_ID,
+ mMockTransport, request, deniedTags, Runnable::run, mStateCallbackList);
+ ISipDelegateStateCallback cb = createDelegateCaptureStateCallback(request, connection);
+ assertNotNull(cb);
+ cb.onCreated(mMockDelegate, null /*denied*/);
+ verify(mMockCreatedCallback).accept(mMockDelegate, deniedTags);
+
+ // call Destroy on the SipDelegate
+ destroy(connection, SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ cb.onDestroyed(SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ verify(mMockDestroyedCallback).accept(
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ }
+
+ @SmallTest
+ @Test
+ public void testDestroyConnectionDead() throws Exception {
+ DelegateRequest request = getDelegateRequest();
+ ArraySet<FeatureTagState> deniedTags = getMmTelDeniedTag();
+ SipDelegateBinderConnection connection = new SipDelegateBinderConnection(TEST_SUB_ID,
+ mMockTransport, request, deniedTags, Runnable::run, mStateCallbackList);
+ ISipDelegateStateCallback cb = createDelegateCaptureStateCallback(request, connection);
+ assertNotNull(cb);
+ cb.onCreated(mMockDelegate, null /*denied*/);
+ verify(mMockCreatedCallback).accept(mMockDelegate, deniedTags);
+
+ // try to destroy when dead and ensure callback is still called.
+ doThrow(new RemoteException()).when(mMockTransport).destroySipDelegate(any(), anyInt());
+ destroy(connection, SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ verify(mMockDestroyedCallback).accept(
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ }
+
+ @SmallTest
+ @Test
+ public void testStateCallback() throws Exception {
+ DelegateRequest request = getDelegateRequest();
+ ArraySet<FeatureTagState> deniedTags = getMmTelDeniedTag();
+ SipDelegateBinderConnection connection = new SipDelegateBinderConnection(TEST_SUB_ID,
+ mMockTransport, request, deniedTags, Runnable::run, mStateCallbackList);
+ ISipDelegateStateCallback cb = createDelegateCaptureStateCallback(request, connection);
+ assertNotNull(cb);
+ cb.onCreated(mMockDelegate, new ArrayList<>(deniedTags));
+ verify(mMockCreatedCallback).accept(mMockDelegate, deniedTags);
+
+ SipDelegateImsConfiguration config = new SipDelegateImsConfiguration.Builder(1).build();
+ cb.onImsConfigurationChanged(config);
+ verify(mMockStateCallback).onImsConfigurationChanged(config);
+
+ DelegateRegistrationState regState = new DelegateRegistrationState.Builder()
+ .addRegisteredFeatureTags(request.getFeatureTags()).build();
+ cb.onFeatureTagRegistrationChanged(regState);
+ verify(mMockStateCallback).onRegistrationStateChanged(regState);
+ }
+
+ private ISipDelegateStateCallback createDelegateCaptureStateCallback(
+ DelegateRequest r, SipDelegateBinderConnection c) throws Exception {
+ boolean isCreating = c.create(mMessageCallback, mMockCreatedCallback);
+ if (!isCreating) return null;
+ ArgumentCaptor<ISipDelegateStateCallback> stateCaptor =
+ ArgumentCaptor.forClass(ISipDelegateStateCallback.class);
+ verify(mMockTransport).createSipDelegate(eq(TEST_SUB_ID), eq(r), stateCaptor.capture(),
+ eq(mMessageCallback));
+ assertNotNull(stateCaptor.getValue());
+ return stateCaptor.getValue();
+ }
+
+ private void destroy(SipDelegateBinderConnection c, int reason) throws Exception {
+ c.destroy(reason, mMockDestroyedCallback);
+ verify(mMockTransport).destroySipDelegate(mMockDelegate, reason);
+ }
+
+ private DelegateRequest getDelegateRequest() {
+ ArraySet<String> featureTags = new ArraySet<>(2);
+ featureTags.add(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG);
+ featureTags.add(ImsSignallingUtils.GROUP_CHAT_TAG);
+ return new DelegateRequest(featureTags);
+ }
+
+ private ArraySet<FeatureTagState> getMmTelDeniedTag() {
+ ArraySet<FeatureTagState> deniedTags = new ArraySet<>();
+ deniedTags.add(new FeatureTagState(ImsSignallingUtils.MMTEL_TAG,
+ SipDelegateManager.DENIED_REASON_NOT_ALLOWED));
+ return deniedTags;
+ }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/SipDelegateControllerTest.java b/tests/src/com/android/services/telephony/rcs/SipDelegateControllerTest.java
new file mode 100644
index 0000000..47b4808
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/SipDelegateControllerTest.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2020 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 com.android.services.telephony.rcs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.aidl.ISipTransport;
+import android.util.ArraySet;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.TestExecutorService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+@RunWith(AndroidJUnit4.class)
+public class SipDelegateControllerTest extends TelephonyTestBase {
+ private static final int TEST_SUB_ID = 1;
+
+ @Mock private ISipDelegate mMockSipDelegate;
+ @Mock private ISipTransport mMockSipTransport;
+ @Mock private MessageTransportStateTracker mMockMessageTracker;
+ @Mock private ISipDelegateMessageCallback mMockMessageCallback;
+ @Mock private DelegateStateTracker mMockDelegateStateTracker;
+ @Mock private DelegateBinderStateManager mMockBinderConnection;
+ @Captor private ArgumentCaptor<BiConsumer<ISipDelegate, Set<FeatureTagState>>> mCreatedCaptor;
+ @Captor private ArgumentCaptor<Consumer<Boolean>> mBooleanConsumerCaptor;
+ @Captor private ArgumentCaptor<Consumer<Integer>> mIntegerConsumerCaptor;
+
+ private ScheduledExecutorService mExecutorService;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ when(mMockMessageTracker.getMessageCallback()).thenReturn(mMockMessageCallback);
+ mExecutorService = new TestExecutorService();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mExecutorService.shutdownNow();
+ super.tearDown();
+ }
+
+ @SmallTest
+ @Test
+ public void testCreateDelegate() throws Exception {
+ DelegateRequest request = getBaseDelegateRequest();
+ SipDelegateController controller = getTestDelegateController(request,
+ Collections.emptySet());
+
+ doReturn(true).when(mMockBinderConnection).create(eq(mMockMessageCallback), any());
+ CompletableFuture<Boolean> future = controller.create(request.getFeatureTags(),
+ Collections.emptySet() /*denied tags*/);
+ BiConsumer<ISipDelegate, Set<FeatureTagState>> consumer =
+ verifyConnectionCreated(1);
+ assertNotNull(consumer);
+
+ assertFalse(future.isDone());
+ consumer.accept(mMockSipDelegate, Collections.emptySet());
+ assertTrue(future.get());
+ verify(mMockMessageTracker).openTransport(mMockSipDelegate, Collections.emptySet());
+ verify(mMockDelegateStateTracker).sipDelegateConnected(Collections.emptySet());
+ }
+
+ @SmallTest
+ @Test
+ public void testCreateDelegateTransportDied() throws Exception {
+ DelegateRequest request = getBaseDelegateRequest();
+ SipDelegateController controller = getTestDelegateController(request,
+ Collections.emptySet());
+
+ //Create operation fails
+ doReturn(false).when(mMockBinderConnection).create(eq(mMockMessageCallback), any());
+ CompletableFuture<Boolean> future = controller.create(request.getFeatureTags(),
+ Collections.emptySet() /*denied tags*/);
+
+ assertFalse(future.get());
+ }
+
+ @SmallTest
+ @Test
+ public void testDestroyDelegate() throws Exception {
+ DelegateRequest request = getBaseDelegateRequest();
+ SipDelegateController controller = getTestDelegateController(request,
+ Collections.emptySet());
+ createSipDelegate(request, controller);
+
+ CompletableFuture<Integer> pendingDestroy = controller.destroy(false /*force*/,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ assertFalse(pendingDestroy.isDone());
+ Consumer<Boolean> pendingClosedConsumer = verifyMessageTrackerCloseGracefully();
+ verify(mMockDelegateStateTracker).sipDelegateChanging(
+ DelegateRegistrationState.DEREGISTERING_REASON_DESTROY_PENDING);
+
+ // verify we do not call destroy on the delegate until the message tracker releases the
+ // transport.
+ verify(mMockBinderConnection, never()).destroy(anyInt(), any());
+ pendingClosedConsumer.accept(true);
+ Consumer<Integer> pendingDestroyedConsumer = verifyBinderConnectionDestroy();
+ pendingDestroyedConsumer.accept(
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ verify(mMockDelegateStateTracker).sipDelegateDestroyed(
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ assertTrue(pendingDestroy.isDone());
+ assertEquals(SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP,
+ pendingDestroy.get().intValue());
+ }
+
+ @SmallTest
+ @Test
+ public void testDestroyDelegateForce() throws Exception {
+ DelegateRequest request = getBaseDelegateRequest();
+ SipDelegateController controller = getTestDelegateController(request,
+ Collections.emptySet());
+ createSipDelegate(request, controller);
+
+ CompletableFuture<Integer> pendingDestroy = controller.destroy(true /*force*/,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ assertFalse(pendingDestroy.isDone());
+ // Do not wait for message transport close in this case.
+ verify(mMockMessageTracker).close(
+ SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED);
+ verify(mMockDelegateStateTracker, never()).sipDelegateChanging(
+ DelegateRegistrationState.DEREGISTERING_REASON_DESTROY_PENDING);
+
+ //verify destroy is called
+ Consumer<Integer> pendingDestroyedConsumer = verifyBinderConnectionDestroy();
+ pendingDestroyedConsumer.accept(
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ verify(mMockDelegateStateTracker).sipDelegateDestroyed(
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ assertTrue(pendingDestroy.isDone());
+ assertEquals(SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP,
+ pendingDestroy.get().intValue());
+ }
+
+ @SmallTest
+ @Test
+ public void testChangeSupportedFeatures() throws Exception {
+ DelegateRequest request = getBaseDelegateRequest();
+ SipDelegateController controller = getTestDelegateController(request,
+ Collections.emptySet());
+ createSipDelegate(request, controller);
+
+ Set<String> newFts = getBaseFTSet();
+ newFts.add(ImsSignallingUtils.GROUP_CHAT_TAG);
+ CompletableFuture<Boolean> pendingChange = controller.changeSupportedFeatureTags(
+ newFts, Collections.emptySet());
+ assertFalse(pendingChange.isDone());
+ // message tracker should close gracefully.
+ Consumer<Boolean> pendingClosedConsumer = verifyMessageTrackerCloseGracefully();
+ verify(mMockDelegateStateTracker).sipDelegateChanging(
+ DelegateRegistrationState.DEREGISTERING_REASON_FEATURE_TAGS_CHANGING);
+ verify(mMockBinderConnection, never()).destroy(anyInt(), any());
+ pendingClosedConsumer.accept(true);
+ Consumer<Integer> pendingDestroyedConsumer = verifyBinderConnectionDestroy();
+ pendingDestroyedConsumer.accept(
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ verify(mMockDelegateStateTracker, never()).sipDelegateDestroyed(anyInt());
+
+ // This will cause any exceptions to be printed if something completed exceptionally.
+ assertNull(pendingChange.getNow(null));
+ BiConsumer<ISipDelegate, Set<FeatureTagState>> consumer =
+ verifyConnectionCreated(2);
+ assertNotNull(consumer);
+ consumer.accept(mMockSipDelegate, Collections.emptySet());
+ assertTrue(pendingChange.get());
+
+ verify(mMockMessageTracker, times(2)).openTransport(mMockSipDelegate,
+ Collections.emptySet());
+ verify(mMockDelegateStateTracker, times(2)).sipDelegateConnected(Collections.emptySet());
+ }
+
+ private void createSipDelegate(DelegateRequest request, SipDelegateController controller)
+ throws Exception {
+ doReturn(true).when(mMockBinderConnection).create(eq(mMockMessageCallback), any());
+ CompletableFuture<Boolean> future = controller.create(request.getFeatureTags(),
+ Collections.emptySet() /*denied tags*/);
+ BiConsumer<ISipDelegate, Set<FeatureTagState>> consumer =
+ verifyConnectionCreated(1);
+ assertNotNull(consumer);
+ consumer.accept(mMockSipDelegate, Collections.emptySet());
+ assertTrue(future.get());
+ }
+
+ private ArraySet<String> getBaseFTSet() {
+ ArraySet<String> request = new ArraySet<>();
+ request.add(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG);
+ return request;
+ }
+
+ private DelegateRequest getBaseDelegateRequest() {
+ return new DelegateRequest(getBaseFTSet());
+ }
+
+ private SipDelegateController getTestDelegateController(DelegateRequest request,
+ Set<FeatureTagState> deniedSet) {
+ return new SipDelegateController(TEST_SUB_ID, request, "", mMockSipTransport,
+ mExecutorService, mMockMessageTracker, mMockDelegateStateTracker,
+ (a, b, c, deniedFeatureSet, e, f) -> {
+ assertEquals(deniedSet, deniedFeatureSet);
+ return mMockBinderConnection;
+ });
+ }
+
+ private BiConsumer<ISipDelegate, Set<FeatureTagState>> verifyConnectionCreated(int numTimes) {
+ verify(mMockBinderConnection, times(numTimes)).create(eq(mMockMessageCallback),
+ mCreatedCaptor.capture());
+ return mCreatedCaptor.getValue();
+ }
+
+ private Consumer<Boolean> verifyMessageTrackerCloseGracefully() {
+ verify(mMockMessageTracker).closeGracefully(anyInt(), anyInt(),
+ mBooleanConsumerCaptor.capture());
+ return mBooleanConsumerCaptor.getValue();
+ }
+ private Consumer<Integer> verifyBinderConnectionDestroy() {
+ verify(mMockBinderConnection).destroy(anyInt(), mIntegerConsumerCaptor.capture());
+ return mIntegerConsumerCaptor.getValue();
+ }
+
+}
diff --git a/tests/src/com/android/services/telephony/rcs/SipTransportControllerTest.java b/tests/src/com/android/services/telephony/rcs/SipTransportControllerTest.java
new file mode 100644
index 0000000..8e10757
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/SipTransportControllerTest.java
@@ -0,0 +1,844 @@
+/*
+ * Copyright (C) 2020 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 com.android.services.telephony.rcs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.role.RoleManager;
+import android.os.IBinder;
+import android.os.UserHandle;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateConnectionStateCallback;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.aidl.ISipTransport;
+import android.util.ArraySet;
+import android.util.Pair;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.TestExecutorService;
+import com.android.ims.RcsFeatureManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+@RunWith(AndroidJUnit4.class)
+public class SipTransportControllerTest extends TelephonyTestBase {
+ private static final int TEST_SUB_ID = 1;
+ private static final String TEST_PACKAGE_NAME = "com.test_pkg";
+ private static final String TEST_PACKAGE_NAME_2 = "com.test_pkg2";
+ private static final int TIMEOUT_MS = 200;
+ private static final int THROTTLE_MS = 50;
+
+ private class SipDelegateControllerContainer {
+ public final int subId;
+ public final String packageName;
+ public final DelegateRequest delegateRequest;
+ public final SipDelegateController delegateController;
+ public final ISipDelegate mMockDelegate;
+ public final IBinder mMockDelegateBinder;
+
+ SipDelegateControllerContainer(int id, String name, DelegateRequest request) {
+ delegateController = mock(SipDelegateController.class);
+ mMockDelegate = mock(ISipDelegate.class);
+ mMockDelegateBinder = mock(IBinder.class);
+ doReturn(mMockDelegateBinder).when(mMockDelegate).asBinder();
+ doReturn(name).when(delegateController).getPackageName();
+ doReturn(request).when(delegateController).getInitialRequest();
+ doReturn(mMockDelegate).when(delegateController).getSipDelegateInterface();
+ subId = id;
+ packageName = name;
+ delegateRequest = request;
+ }
+ }
+
+ @Mock private RcsFeatureManager mRcsManager;
+ @Mock private ISipTransport mSipTransport;
+ @Mock private ISipDelegateConnectionStateCallback mMockStateCallback;
+ @Mock private ISipDelegateMessageCallback mMockMessageCallback;
+ @Mock private SipTransportController.SipDelegateControllerFactory
+ mMockDelegateControllerFactory;
+ @Mock private SipTransportController.RoleManagerAdapter mMockRoleManager;
+
+ private ScheduledExecutorService mExecutorService = null;
+ private final ArrayList<SipDelegateControllerContainer> mMockControllers = new ArrayList<>();
+ private final ArrayList<String> mSmsPackageName = new ArrayList<>(1);
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ doReturn(mSmsPackageName).when(mMockRoleManager).getRoleHolders(RoleManager.ROLE_SMS);
+ mSmsPackageName.add(TEST_PACKAGE_NAME);
+ doAnswer(invocation -> {
+ Integer subId = invocation.getArgument(0);
+ String packageName = invocation.getArgument(2);
+ DelegateRequest request = invocation.getArgument(1);
+ SipDelegateController c = getMockDelegateController(subId, packageName, request);
+ assertNotNull("create called with no corresponding controller set up", c);
+ return c;
+ }).when(mMockDelegateControllerFactory).create(anyInt(), any(), anyString(), any(),
+ any(), any(), any());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ boolean isShutdown = mExecutorService == null || mExecutorService.isShutdown();
+ if (!isShutdown) {
+ mExecutorService.shutdownNow();
+ }
+ }
+
+ @SmallTest
+ @Test
+ public void isSupportedRcsNotConnected() {
+ SipTransportController controller = createController(new TestExecutorService());
+ try {
+ controller.isSupported(TEST_SUB_ID);
+ fail();
+ } catch (ImsException e) {
+ assertEquals(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE, e.getCode());
+ }
+ }
+
+ @SmallTest
+ @Test
+ public void isSupportedInvalidSubId() {
+ SipTransportController controller = createController(new TestExecutorService());
+ try {
+ controller.isSupported(TEST_SUB_ID + 1);
+ fail();
+ } catch (ImsException e) {
+ assertEquals(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION, e.getCode());
+ }
+ }
+
+ @SmallTest
+ @Test
+ public void isSupportedSubIdChanged() {
+ SipTransportController controller = createController(new TestExecutorService());
+ controller.onAssociatedSubscriptionUpdated(TEST_SUB_ID + 1);
+ try {
+ controller.isSupported(TEST_SUB_ID);
+ fail();
+ } catch (ImsException e) {
+ assertEquals(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION, e.getCode());
+ }
+ }
+
+ @SmallTest
+ @Test
+ public void isSupportedSipTransportAvailableRcsConnected() throws Exception {
+ SipTransportController controller = createController(new TestExecutorService());
+ doReturn(mSipTransport).when(mRcsManager).getSipTransport();
+ controller.onRcsConnected(mRcsManager);
+ try {
+ assertTrue(controller.isSupported(TEST_SUB_ID));
+ } catch (ImsException e) {
+ fail();
+ }
+ }
+
+ @SmallTest
+ @Test
+ public void isSupportedSipTransportNotAvailableRcsDisconnected() throws Exception {
+ SipTransportController controller = createController(new TestExecutorService());
+ doReturn(mSipTransport).when(mRcsManager).getSipTransport();
+ controller.onRcsConnected(mRcsManager);
+ controller.onRcsDisconnected();
+ try {
+ controller.isSupported(TEST_SUB_ID);
+ fail();
+ } catch (ImsException e) {
+ assertEquals(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE, e.getCode());
+ }
+ }
+
+ @SmallTest
+ @Test
+ public void isSupportedSipTransportNotAvailableRcsConnected() throws Exception {
+ SipTransportController controller = createController(new TestExecutorService());
+ doReturn(null).when(mRcsManager).getSipTransport();
+ controller.onRcsConnected(mRcsManager);
+ try {
+ assertFalse(controller.isSupported(TEST_SUB_ID));
+ } catch (ImsException e) {
+ fail();
+ }
+ }
+
+ @SmallTest
+ @Test
+ public void isSupportedImsServiceNotAvailableRcsConnected() throws Exception {
+ SipTransportController controller = createController(new TestExecutorService());
+ doThrow(new ImsException("", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE))
+ .when(mRcsManager).getSipTransport();
+ controller.onRcsConnected(mRcsManager);
+ try {
+ controller.isSupported(TEST_SUB_ID);
+ fail();
+ } catch (ImsException e) {
+ assertEquals(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE, e.getCode());
+ }
+ }
+
+ @SmallTest
+ @Test
+ public void createImsServiceAvailableSubIdIncorrect() throws Exception {
+ SipTransportController controller = createController(new TestExecutorService());
+ doReturn(mSipTransport).when(mRcsManager).getSipTransport();
+ controller.onRcsConnected(mRcsManager);
+ try {
+ controller.createSipDelegate(TEST_SUB_ID + 1,
+ new DelegateRequest(Collections.emptySet()), TEST_PACKAGE_NAME,
+ mMockStateCallback, mMockMessageCallback);
+ fail();
+ } catch (ImsException e) {
+ assertEquals(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION, e.getCode());
+ }
+ }
+
+ @SmallTest
+ @Test
+ public void createImsServiceDoesntSupportTransport() throws Exception {
+ SipTransportController controller = createController(new TestExecutorService());
+ doReturn(null).when(mRcsManager).getSipTransport();
+ controller.onRcsConnected(mRcsManager);
+ try {
+ controller.createSipDelegate(TEST_SUB_ID,
+ new DelegateRequest(Collections.emptySet()), TEST_PACKAGE_NAME,
+ mMockStateCallback, mMockMessageCallback);
+ fail();
+ } catch (ImsException e) {
+ assertEquals(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, e.getCode());
+ }
+ }
+
+ @SmallTest
+ @Test
+ public void createImsServiceNotAvailable() throws Exception {
+ SipTransportController controller = createController(new TestExecutorService());
+ doThrow(new ImsException("", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE))
+ .when(mRcsManager).getSipTransport();
+ // No RCS connected message
+ try {
+ controller.createSipDelegate(TEST_SUB_ID,
+ new DelegateRequest(Collections.emptySet()), TEST_PACKAGE_NAME,
+ mMockStateCallback, mMockMessageCallback);
+ fail();
+ } catch (ImsException e) {
+ assertEquals(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE, e.getCode());
+ }
+ }
+
+ @SmallTest
+ @Test
+ public void basicCreate() throws Exception {
+ SipTransportController controller = setupLiveTransportController();
+
+ DelegateRequest r = getBaseDelegateRequest();
+
+ SipDelegateController c = injectMockDelegateController(TEST_PACKAGE_NAME, r);
+ createDelegateAndVerify(controller, c, r, r.getFeatureTags(), Collections.emptySet(),
+ TEST_PACKAGE_NAME);
+ }
+
+ @SmallTest
+ @Test
+ public void basicCreateDestroy() throws Exception {
+ SipTransportController controller = setupLiveTransportController();
+
+ DelegateRequest r = getBaseDelegateRequest();
+ SipDelegateController c = injectMockDelegateController(TEST_PACKAGE_NAME, r);
+ createDelegateAndVerify(controller, c, r, r.getFeatureTags(), Collections.emptySet(),
+ TEST_PACKAGE_NAME);
+
+ destroyDelegateAndVerify(controller, c, false,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ }
+
+ @SmallTest
+ @Test
+ public void testCreateButNotInRole() throws Exception {
+ SipTransportController controller = setupLiveTransportController();
+
+ DelegateRequest r = getBaseDelegateRequest();
+ Set<FeatureTagState> getDeniedTags = getDeniedTagsForReason(r.getFeatureTags(),
+ SipDelegateManager.DENIED_REASON_NOT_ALLOWED);
+
+ // Try to create a SipDelegate for a package that is not the default sms role.
+ SipDelegateController c = injectMockDelegateController(TEST_PACKAGE_NAME_2, r);
+ createDelegateAndVerify(controller, c, r, Collections.emptySet(), getDeniedTags,
+ TEST_PACKAGE_NAME_2);
+ }
+
+ @SmallTest
+ @Test
+ public void createTwoAndDenyOverlappingTags() throws Exception {
+ SipTransportController controller = setupLiveTransportController();
+
+ // First delegate requests RCS message + File transfer
+ ArraySet<String> firstDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
+ firstDelegate.remove(ImsSignallingUtils.GROUP_CHAT_TAG);
+ DelegateRequest firstDelegateRequest = new DelegateRequest(firstDelegate);
+ SipDelegateController c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
+ firstDelegateRequest);
+ createDelegateAndVerify(controller, c1, firstDelegateRequest, firstDelegate,
+ Collections.emptySet(), TEST_PACKAGE_NAME);
+
+ // First delegate requests RCS message + Group RCS message. For this delegate, single RCS
+ // message should be denied.
+ ArraySet<String> secondDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
+ secondDelegate.remove(ImsSignallingUtils.FILE_TRANSFER_HTTP_TAG);
+ DelegateRequest secondDelegateRequest = new DelegateRequest(secondDelegate);
+ Pair<Set<String>, Set<FeatureTagState>> grantedAndDenied = getAllowedAndDeniedTagsForConfig(
+ secondDelegateRequest, SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE,
+ firstDelegate);
+ SipDelegateController c2 = injectMockDelegateController(TEST_PACKAGE_NAME,
+ secondDelegateRequest);
+ createDelegateAndVerify(controller, c2, secondDelegateRequest, grantedAndDenied.first,
+ grantedAndDenied.second, TEST_PACKAGE_NAME, 1);
+ }
+
+ @SmallTest
+ @Test
+ public void createTwoAndTriggerRoleChange() throws Exception {
+ SipTransportController controller = setupLiveTransportController();
+
+ DelegateRequest firstDelegateRequest = getBaseDelegateRequest();
+ Set<FeatureTagState> firstDeniedTags = getDeniedTagsForReason(
+ firstDelegateRequest.getFeatureTags(),
+ SipDelegateManager.DENIED_REASON_NOT_ALLOWED);
+ SipDelegateController c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
+ firstDelegateRequest);
+ createDelegateAndVerify(controller, c1, firstDelegateRequest,
+ firstDelegateRequest.getFeatureTags(), Collections.emptySet(), TEST_PACKAGE_NAME);
+
+ DelegateRequest secondDelegateRequest = getBaseDelegateRequest();
+ Set<FeatureTagState> secondDeniedTags = getDeniedTagsForReason(
+ secondDelegateRequest.getFeatureTags(),
+ SipDelegateManager.DENIED_REASON_NOT_ALLOWED);
+ // Try to create a SipDelegate for a package that is not the default sms role.
+ SipDelegateController c2 = injectMockDelegateController(TEST_PACKAGE_NAME_2,
+ secondDelegateRequest);
+ createDelegateAndVerify(controller, c2, secondDelegateRequest, Collections.emptySet(),
+ secondDeniedTags, TEST_PACKAGE_NAME_2, 1);
+
+ // now swap the SMS role.
+ CompletableFuture<Boolean> pendingC1Change = setChangeSupportedFeatureTagsFuture(c1,
+ Collections.emptySet(), firstDeniedTags);
+ CompletableFuture<Boolean> pendingC2Change = setChangeSupportedFeatureTagsFuture(c2,
+ secondDelegateRequest.getFeatureTags(), Collections.emptySet());
+ setSmsRoleAndEvaluate(controller, TEST_PACKAGE_NAME_2);
+ // trigger completion stage to run
+ waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+ verify(c1).changeSupportedFeatureTags(Collections.emptySet(), firstDeniedTags);
+ // we should not get a change for c2 until pendingC1Change completes.
+ verify(c2, never()).changeSupportedFeatureTags(secondDelegateRequest.getFeatureTags(),
+ Collections.emptySet());
+ // ensure we are not blocking executor here
+ waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+ completePendingChange(pendingC1Change, true);
+ // trigger completion stage to run
+ waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+ verify(c2).changeSupportedFeatureTags(secondDelegateRequest.getFeatureTags(),
+ Collections.emptySet());
+ // ensure we are not blocking executor here
+ waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+ completePendingChange(pendingC2Change, true);
+ }
+
+ @SmallTest
+ @Test
+ public void createTwoAndDestroyOlder() throws Exception {
+ SipTransportController controller = setupLiveTransportController();
+
+ // First delegate requests RCS message + File transfer
+ ArraySet<String> firstDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
+ firstDelegate.remove(ImsSignallingUtils.GROUP_CHAT_TAG);
+ DelegateRequest firstDelegateRequest = new DelegateRequest(firstDelegate);
+ SipDelegateController c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
+ firstDelegateRequest);
+ createDelegateAndVerify(controller, c1, firstDelegateRequest, firstDelegate,
+ Collections.emptySet(), TEST_PACKAGE_NAME);
+
+ // First delegate requests RCS message + Group RCS message. For this delegate, single RCS
+ // message should be denied.
+ ArraySet<String> secondDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
+ secondDelegate.remove(ImsSignallingUtils.FILE_TRANSFER_HTTP_TAG);
+ DelegateRequest secondDelegateRequest = new DelegateRequest(secondDelegate);
+ Pair<Set<String>, Set<FeatureTagState>> grantedAndDenied = getAllowedAndDeniedTagsForConfig(
+ secondDelegateRequest, SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE,
+ firstDelegate);
+ SipDelegateController c2 = injectMockDelegateController(TEST_PACKAGE_NAME,
+ secondDelegateRequest);
+ createDelegateAndVerify(controller, c2, secondDelegateRequest, grantedAndDenied.first,
+ grantedAndDenied.second, TEST_PACKAGE_NAME, 1);
+
+ // Destroy the firstDelegate, which should now cause all previously denied tags to be
+ // granted to the new delegate.
+ CompletableFuture<Boolean> pendingC2Change = setChangeSupportedFeatureTagsFuture(c2,
+ secondDelegate, Collections.emptySet());
+ destroyDelegateAndVerify(controller, c1, false /*force*/,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ // wait for create to be processed.
+ assertTrue(waitForExecutorAction(mExecutorService, TIMEOUT_MS));
+ verify(c2).changeSupportedFeatureTags(secondDelegate, Collections.emptySet());
+ completePendingChange(pendingC2Change, true);
+ }
+
+ @SmallTest
+ @Test
+ public void testThrottling() throws Exception {
+ SipTransportController controller = setupLiveTransportController(THROTTLE_MS);
+
+ // First delegate requests RCS message + File transfer
+ ArraySet<String> firstDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
+ firstDelegate.remove(ImsSignallingUtils.GROUP_CHAT_TAG);
+ DelegateRequest firstDelegateRequest = new DelegateRequest(firstDelegate);
+ SipDelegateController c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
+ firstDelegateRequest);
+ CompletableFuture<Boolean> pendingC1Change = createDelegate(controller, c1,
+ firstDelegateRequest, firstDelegate, Collections.emptySet(), TEST_PACKAGE_NAME);
+
+ // Request RCS message + group RCS Message. For this delegate, single RCS message should be
+ // denied.
+ ArraySet<String> secondDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
+ secondDelegate.remove(ImsSignallingUtils.FILE_TRANSFER_HTTP_TAG);
+ DelegateRequest secondDelegateRequest = new DelegateRequest(secondDelegate);
+ Pair<Set<String>, Set<FeatureTagState>> grantedAndDeniedC2 =
+ getAllowedAndDeniedTagsForConfig(secondDelegateRequest,
+ SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE, firstDelegate);
+ SipDelegateController c2 = injectMockDelegateController(TEST_PACKAGE_NAME,
+ secondDelegateRequest);
+ CompletableFuture<Boolean> pendingC2Change = createDelegate(controller, c2,
+ secondDelegateRequest, grantedAndDeniedC2.first, grantedAndDeniedC2.second,
+ TEST_PACKAGE_NAME);
+
+ // Request group RCS message + file transfer. All should be denied at first
+ ArraySet<String> thirdDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
+ thirdDelegate.remove(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG);
+ DelegateRequest thirdDelegateRequest = new DelegateRequest(thirdDelegate);
+ Pair<Set<String>, Set<FeatureTagState>> grantedAndDeniedC3 =
+ getAllowedAndDeniedTagsForConfig(thirdDelegateRequest,
+ SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE, firstDelegate,
+ grantedAndDeniedC2.first);
+ SipDelegateController c3 = injectMockDelegateController(TEST_PACKAGE_NAME,
+ thirdDelegateRequest);
+ CompletableFuture<Boolean> pendingC3Change = createDelegate(controller, c3,
+ thirdDelegateRequest, grantedAndDeniedC3.first, grantedAndDeniedC3.second,
+ TEST_PACKAGE_NAME);
+
+ assertTrue(scheduleDelayedWait(2 * THROTTLE_MS));
+ verifyDelegateChanged(c1, pendingC1Change, firstDelegate, Collections.emptySet(), 0);
+ verifyDelegateChanged(c2, pendingC2Change, grantedAndDeniedC2.first,
+ grantedAndDeniedC2.second, 0);
+ verifyDelegateChanged(c3, pendingC3Change, grantedAndDeniedC3.first,
+ grantedAndDeniedC3.second, 0);
+
+ // Destroy the first and second controller in quick succession, this should only generate
+ // one reevaluate for the third controller.
+ CompletableFuture<Boolean> pendingChangeC3 = setChangeSupportedFeatureTagsFuture(
+ c3, thirdDelegate, Collections.emptySet());
+ CompletableFuture<Integer> pendingDestroyC1 = destroyDelegate(controller, c1,
+ false /*force*/,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ CompletableFuture<Integer> pendingDestroyC2 = destroyDelegate(controller, c2,
+ false /*force*/,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ assertTrue(scheduleDelayedWait(2 * THROTTLE_MS));
+ verifyDestroyDelegate(controller, c1, pendingDestroyC1, false /*force*/,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ verifyDestroyDelegate(controller, c2, pendingDestroyC2, false /*force*/,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+
+ // All requested features should now be granted
+ completePendingChange(pendingChangeC3, true);
+ verify(c3).changeSupportedFeatureTags(thirdDelegate, Collections.emptySet());
+ // In total reeval should have only been called twice.
+ verify(c3, times(2)).changeSupportedFeatureTags(any(), any());
+ }
+
+ @SmallTest
+ @Test
+ public void testSubIdChangeDestroyTriggered() throws Exception {
+ SipTransportController controller = setupLiveTransportController();
+
+ ArraySet<String> firstDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
+ DelegateRequest firstDelegateRequest = new DelegateRequest(firstDelegate);
+ SipDelegateController c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
+ firstDelegateRequest);
+ createDelegateAndVerify(controller, c1, firstDelegateRequest, firstDelegate,
+ Collections.emptySet(), TEST_PACKAGE_NAME);
+
+ CompletableFuture<Integer> pendingDestroy = setDestroyFuture(c1, true,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
+ controller.onAssociatedSubscriptionUpdated(TEST_SUB_ID + 1);
+ waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+ verifyDestroyDelegate(controller, c1, pendingDestroy, true /*force*/,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
+ }
+
+ @SmallTest
+ @Test
+ public void testRcsManagerGoneDestroyTriggered() throws Exception {
+ SipTransportController controller = setupLiveTransportController();
+
+ ArraySet<String> firstDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
+ DelegateRequest firstDelegateRequest = new DelegateRequest(firstDelegate);
+ SipDelegateController c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
+ firstDelegateRequest);
+ createDelegateAndVerify(controller, c1, firstDelegateRequest, firstDelegate,
+ Collections.emptySet(), TEST_PACKAGE_NAME);
+
+ CompletableFuture<Integer> pendingDestroy = setDestroyFuture(c1, true,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+ controller.onRcsDisconnected();
+ waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+ verifyDestroyDelegate(controller, c1, pendingDestroy, true /*force*/,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+ }
+
+ @SmallTest
+ @Test
+ public void testDestroyTriggered() throws Exception {
+ SipTransportController controller = setupLiveTransportController();
+
+ ArraySet<String> firstDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
+ DelegateRequest firstDelegateRequest = new DelegateRequest(firstDelegate);
+ SipDelegateController c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
+ firstDelegateRequest);
+ createDelegateAndVerify(controller, c1, firstDelegateRequest, firstDelegate,
+ Collections.emptySet(), TEST_PACKAGE_NAME);
+
+ CompletableFuture<Integer> pendingDestroy = setDestroyFuture(c1, true,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
+ controller.onDestroy();
+ waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+ // verify change was called.
+ verify(c1).destroy(true /*force*/,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
+ // ensure thread is not blocked while waiting for pending complete.
+ waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+ completePendingDestroy(pendingDestroy,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
+ }
+
+ @SmallTest
+ @Test
+ public void testTimingSubIdChangedAndCreateNewSubId() throws Exception {
+ SipTransportController controller = setupLiveTransportController(THROTTLE_MS);
+
+ ArraySet<String> firstDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
+ DelegateRequest firstDelegateRequest = new DelegateRequest(firstDelegate);
+ SipDelegateController c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
+ firstDelegateRequest);
+ CompletableFuture<Boolean> pendingC1Change = createDelegate(controller, c1,
+ firstDelegateRequest, firstDelegate, Collections.emptySet(), TEST_PACKAGE_NAME);
+ assertTrue(scheduleDelayedWait(2 * THROTTLE_MS));
+ verifyDelegateChanged(c1, pendingC1Change, firstDelegate, Collections.emptySet(), 0);
+
+
+ CompletableFuture<Integer> pendingDestroy = setDestroyFuture(c1, true,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
+ // triggers reeval now.
+ controller.onAssociatedSubscriptionUpdated(TEST_SUB_ID + 1);
+ waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+
+ // mock a second delegate with the new subId associated with the slot.
+ ArraySet<String> secondDelegate = new ArraySet<>();
+ secondDelegate.add(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG);
+ secondDelegate.add(ImsSignallingUtils.FILE_TRANSFER_HTTP_TAG);
+ DelegateRequest secondDelegateRequest = new DelegateRequest(secondDelegate);
+ SipDelegateController c2 = injectMockDelegateController(TEST_SUB_ID + 1,
+ TEST_PACKAGE_NAME, secondDelegateRequest);
+ CompletableFuture<Boolean> pendingC2Change = createDelegate(controller, c2,
+ TEST_SUB_ID + 1, secondDelegateRequest, secondDelegate,
+ Collections.emptySet(), TEST_PACKAGE_NAME);
+ assertTrue(scheduleDelayedWait(THROTTLE_MS));
+
+ //trigger destroyed event
+ verifyDestroyDelegate(controller, c1, pendingDestroy, true /*force*/,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
+ assertTrue(scheduleDelayedWait(2 * THROTTLE_MS));
+ verifyDelegateChanged(c2, pendingC2Change, secondDelegate, Collections.emptySet(), 0);
+ }
+
+ @SafeVarargs
+ private final Pair<Set<String>, Set<FeatureTagState>> getAllowedAndDeniedTagsForConfig(
+ DelegateRequest r, int denyReason, Set<String>... previousRequestedTagSets) {
+ ArraySet<String> rejectedTags = new ArraySet<>(r.getFeatureTags());
+ ArraySet<String> grantedTags = new ArraySet<>(r.getFeatureTags());
+ Set<String> previousRequestedTags = new ArraySet<>();
+ for (Set<String> s : previousRequestedTagSets) {
+ previousRequestedTags.addAll(s);
+ }
+ rejectedTags.retainAll(previousRequestedTags);
+ grantedTags.removeAll(previousRequestedTags);
+ Set<FeatureTagState> deniedTags = getDeniedTagsForReason(rejectedTags, denyReason);
+ return new Pair<>(grantedTags, deniedTags);
+ }
+
+ private void completePendingChange(CompletableFuture<Boolean> change, boolean result) {
+ mExecutorService.execute(() -> change.complete(result));
+ waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+ }
+
+ private void completePendingDestroy(CompletableFuture<Integer> destroy, int result) {
+ mExecutorService.execute(() -> destroy.complete(result));
+ waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+ }
+
+ private SipTransportController setupLiveTransportController() throws Exception {
+ return setupLiveTransportController(0 /*throttleMs*/);
+ }
+
+ private SipTransportController setupLiveTransportController(int throttleMs) throws Exception {
+ mExecutorService = Executors.newSingleThreadScheduledExecutor();
+ SipTransportController controller = createControllerAndThrottle(mExecutorService,
+ throttleMs);
+ doReturn(mSipTransport).when(mRcsManager).getSipTransport();
+ controller.onAssociatedSubscriptionUpdated(TEST_SUB_ID);
+ controller.onRcsConnected(mRcsManager);
+ waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+ return controller;
+ }
+
+ private void createDelegateAndVerify(SipTransportController controller,
+ SipDelegateController delegateController, DelegateRequest r, Set<String> allowedTags,
+ Set<FeatureTagState> deniedTags, String packageName,
+ int numPreviousChanges) throws ImsException {
+
+ CompletableFuture<Boolean> pendingChange = createDelegate(controller, delegateController, r,
+ allowedTags, deniedTags, packageName);
+ verifyDelegateChanged(delegateController, pendingChange, allowedTags, deniedTags,
+ numPreviousChanges);
+ }
+
+ private void createDelegateAndVerify(SipTransportController controller,
+ SipDelegateController delegateController, DelegateRequest r, Set<String> allowedTags,
+ Set<FeatureTagState> deniedTags, String packageName) throws ImsException {
+ createDelegateAndVerify(controller, delegateController, r, allowedTags, deniedTags,
+ packageName, 0);
+ }
+
+ private CompletableFuture<Boolean> createDelegate(SipTransportController controller,
+ SipDelegateController delegateController, int subId, DelegateRequest r,
+ Set<String> allowedTags, Set<FeatureTagState> deniedTags, String packageName) {
+ CompletableFuture<Boolean> pendingChange = setChangeSupportedFeatureTagsFuture(
+ delegateController, allowedTags, deniedTags);
+ try {
+ controller.createSipDelegate(subId, r, packageName, mMockStateCallback,
+ mMockMessageCallback);
+ } catch (ImsException e) {
+ fail("ImsException thrown:" + e);
+ }
+ // move to internal & schedule eval
+ waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+ // reeval
+ waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+ return pendingChange;
+ }
+
+ private CompletableFuture<Boolean> createDelegate(SipTransportController controller,
+ SipDelegateController delegateController, DelegateRequest r, Set<String> allowedTags,
+ Set<FeatureTagState> deniedTags, String packageName) throws ImsException {
+ return createDelegate(controller, delegateController, TEST_SUB_ID, r, allowedTags,
+ deniedTags, packageName);
+ }
+
+ private void verifyDelegateChanged(SipDelegateController delegateController,
+ CompletableFuture<Boolean> pendingChange, Set<String> allowedTags,
+ Set<FeatureTagState> deniedTags, int numPreviousChangeStages) {
+ // empty the queue of pending changeSupportedFeatureTags before running the one we are
+ // interested in, since the reevaluate waits for one stage to complete before moving to the
+ // next.
+ for (int i = 0; i < numPreviousChangeStages + 1; i++) {
+ assertTrue(waitForExecutorAction(mExecutorService, TIMEOUT_MS));
+ }
+ // verify change was called.
+ verify(delegateController).changeSupportedFeatureTags(allowedTags, deniedTags);
+ // ensure thread is not blocked while waiting for pending complete.
+ assertTrue(waitForExecutorAction(mExecutorService, TIMEOUT_MS));
+ completePendingChange(pendingChange, true);
+ // process pending change.
+ assertTrue(waitForExecutorAction(mExecutorService, TIMEOUT_MS));
+ }
+
+ private void destroyDelegateAndVerify(SipTransportController controller,
+ SipDelegateController delegateController, boolean force, int reason) {
+ CompletableFuture<Integer> pendingDestroy = destroyDelegate(controller, delegateController,
+ force, reason);
+ verifyDestroyDelegate(controller, delegateController, pendingDestroy, force, reason);
+ }
+
+ private CompletableFuture<Integer> destroyDelegate(SipTransportController controller,
+ SipDelegateController delegateController, boolean force, int reason) {
+ CompletableFuture<Integer> pendingDestroy = setDestroyFuture(delegateController, force,
+ reason);
+ controller.destroySipDelegate(TEST_SUB_ID, delegateController.getSipDelegateInterface(),
+ reason);
+ // move to internal & schedule eval
+ waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+ // reeval
+ waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+ return pendingDestroy;
+ }
+
+ private void verifyDestroyDelegate(SipTransportController controller,
+ SipDelegateController delegateController, CompletableFuture<Integer> pendingDestroy,
+ boolean force, int reason) {
+ // verify destroy was called.
+ verify(delegateController).destroy(force, reason);
+ // ensure thread is not blocked while waiting for pending complete.
+ waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+ completePendingDestroy(pendingDestroy, reason);
+ }
+
+ private DelegateRequest getBaseDelegateRequest() {
+ Set<String> featureTags = new ArraySet<>();
+ featureTags.add(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG);
+ featureTags.add(ImsSignallingUtils.GROUP_CHAT_TAG);
+ featureTags.add(ImsSignallingUtils.FILE_TRANSFER_HTTP_TAG);
+ return new DelegateRequest(featureTags);
+ }
+
+ private Set<FeatureTagState> getBaseDeniedSet() {
+ Set<FeatureTagState> deniedTags = new ArraySet<>();
+ deniedTags.add(new FeatureTagState(ImsSignallingUtils.MMTEL_TAG,
+ SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE));
+ return deniedTags;
+ }
+
+ private Set<FeatureTagState> getDeniedTagsForReason(Set<String> deniedTags, int reason) {
+ return deniedTags.stream().map(t -> new FeatureTagState(t, reason))
+ .collect(Collectors.toSet());
+ }
+
+ private SipDelegateController injectMockDelegateController(String packageName,
+ DelegateRequest r) {
+ return injectMockDelegateController(TEST_SUB_ID, packageName, r);
+ }
+
+ private SipDelegateController injectMockDelegateController(int subId, String packageName,
+ DelegateRequest r) {
+ SipDelegateControllerContainer c = new SipDelegateControllerContainer(subId,
+ packageName, r);
+ mMockControllers.add(c);
+ return c.delegateController;
+ }
+
+ private SipDelegateController getMockDelegateController(int subId, String packageName,
+ DelegateRequest r) {
+ return mMockControllers.stream()
+ .filter(c -> c.subId == subId && c.packageName.equals(packageName)
+ && c.delegateRequest.equals(r))
+ .map(c -> c.delegateController).findFirst().orElse(null);
+ }
+
+ private CompletableFuture<Boolean> setChangeSupportedFeatureTagsFuture(SipDelegateController c,
+ Set<String> supportedSet, Set<FeatureTagState> deniedSet) {
+ CompletableFuture<Boolean> result = new CompletableFuture<>();
+ doReturn(result).when(c).changeSupportedFeatureTags(eq(supportedSet), eq(deniedSet));
+ return result;
+ }
+
+ private CompletableFuture<Integer> setDestroyFuture(SipDelegateController c, boolean force,
+ int destroyReason) {
+ CompletableFuture<Integer> result = new CompletableFuture<>();
+ doReturn(result).when(c).destroy(force, destroyReason);
+ return result;
+ }
+
+ private void setSmsRoleAndEvaluate(SipTransportController c, String packageName) {
+ verify(mMockRoleManager).addOnRoleHoldersChangedListenerAsUser(any(), any(), any());
+ mSmsPackageName.clear();
+ mSmsPackageName.add(packageName);
+ c.onRoleHoldersChanged(RoleManager.ROLE_SMS, UserHandle.SYSTEM);
+ // finish internal throttled re-evaluate
+ waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+ }
+
+ private SipTransportController createController(ScheduledExecutorService e) {
+ return createControllerAndThrottle(e, 0 /*throttleMs*/);
+ }
+
+ private SipTransportController createControllerAndThrottle(ScheduledExecutorService e,
+ int throttleMs) {
+ return new SipTransportController(mContext, 0 /*slotId*/, TEST_SUB_ID,
+ mMockDelegateControllerFactory, mMockRoleManager,
+ // Remove delays for testing.
+ new SipTransportController.TimerAdapter() {
+ @Override
+ public int getReevaluateThrottleTimerMilliseconds() {
+ return throttleMs;
+ }
+
+ @Override
+ public int getUpdateRegistrationDelayMilliseconds() {
+ return 0;
+ }
+ }, e);
+ }
+
+ private boolean scheduleDelayedWait(long timeMs) {
+ CountDownLatch l = new CountDownLatch(1);
+ mExecutorService.schedule(l::countDown, timeMs, TimeUnit.MILLISECONDS);
+ while (l.getCount() > 0) {
+ try {
+ return l.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ // try again
+ }
+ }
+ return true;
+ }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
index cfb68b7..7a9f9e3 100644
--- a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
+++ b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
@@ -50,8 +50,10 @@
@Captor ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
@Mock TelephonyRcsService.FeatureFactory mFeatureFactory;
- @Mock UserCapabilityExchangeImpl mMockUceSlot0;
- @Mock UserCapabilityExchangeImpl mMockUceSlot1;
+ @Mock UceControllerManager mMockUceSlot0;
+ @Mock UceControllerManager mMockUceSlot1;
+ @Mock SipTransportController mMockSipTransportSlot0;
+ @Mock SipTransportController mMockSipTransportSlot1;
@Mock RcsFeatureController.RegistrationHelperFactory mRegistrationFactory;
@Mock RcsFeatureController.FeatureConnectorFactory<RcsFeatureManager> mFeatureConnectorFactory;
@Mock FeatureConnector<RcsFeatureManager> mFeatureConnector;
@@ -68,10 +70,14 @@
mFeatureControllerSlot1 = createFeatureController(1 /*slotId*/);
doReturn(mFeatureControllerSlot0).when(mFeatureFactory).createController(any(), eq(0));
doReturn(mFeatureControllerSlot1).when(mFeatureFactory).createController(any(), eq(1));
- doReturn(mMockUceSlot0).when(mFeatureFactory).createUserCapabilityExchange(any(), eq(0),
+ doReturn(mMockUceSlot0).when(mFeatureFactory).createUceControllerManager(any(), eq(0),
anyInt());
- doReturn(mMockUceSlot1).when(mFeatureFactory).createUserCapabilityExchange(any(), eq(1),
+ doReturn(mMockUceSlot1).when(mFeatureFactory).createUceControllerManager(any(), eq(1),
anyInt());
+ doReturn(mMockSipTransportSlot0).when(mFeatureFactory).createSipTransportController(any(),
+ eq(0), anyInt());
+ doReturn(mMockSipTransportSlot1).when(mFeatureFactory).createSipTransportController(any(),
+ eq(1), anyInt());
//set up default slot-> sub ID mappings.
setSlotToSubIdMapping(0 /*slotId*/, 1/*subId*/);
setSlotToSubIdMapping(1 /*slotId*/, 2/*subId*/);
@@ -83,18 +89,20 @@
}
@Test
- public void testUserCapabilityExchangePresenceConnected() {
- setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, true /*isEnabled*/);
+ public void testUceControllerPresenceConnected() {
+ setCarrierConfig(1 /*subId*/, CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL,
+ true /*isEnabled*/);
createRcsService(1 /*numSlots*/);
- verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UceControllerManager.class);
verify(mFeatureControllerSlot0).connect();
}
@Test
- public void testUserCapabilityExchangeOptionsConnected() {
- setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL, true /*isEnabled*/);
+ public void testUceControllerOptionsConnected() {
+ setCarrierConfig(1 /*subId*/, CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL,
+ true /*isEnabled*/);
createRcsService(1 /*numSlots*/);
- verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UceControllerManager.class);
verify(mFeatureControllerSlot0).connect();
}
@@ -103,18 +111,67 @@
createRcsService(1 /*numSlots*/);
// No carrier config set for UCE.
verify(mFeatureControllerSlot0, never()).addFeature(mMockUceSlot0,
- UserCapabilityExchangeImpl.class);
+ UceControllerManager.class);
verify(mFeatureControllerSlot0, never()).connect();
}
@Test
+ public void testSipTransportConnected() {
+ createRcsService(1 /*numSlots*/);
+ verify(mFeatureControllerSlot0, never()).addFeature(mMockSipTransportSlot0,
+ SipTransportController.class);
+ verify(mFeatureControllerSlot0, never()).connect();
+
+
+ // Send carrier config update for each slot.
+ setCarrierConfig(1 /*subId*/,
+ CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL,
+ true /*isEnabled*/);
+ sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
+ verify(mFeatureControllerSlot0).addFeature(mMockSipTransportSlot0,
+ SipTransportController.class);
+ verify(mFeatureControllerSlot0).connect();
+ verify(mFeatureControllerSlot0).updateAssociatedSubscription(1);
+ }
+
+ @Test
+ public void testSipTransportConnectedOneSlot() {
+ createRcsService(2 /*numSlots*/);
+ verify(mFeatureControllerSlot0, never()).addFeature(mMockSipTransportSlot0,
+ SipTransportController.class);
+ verify(mFeatureControllerSlot0, never()).connect();
+ verify(mFeatureControllerSlot0, never()).addFeature(mMockSipTransportSlot1,
+ SipTransportController.class);
+ verify(mFeatureControllerSlot1, never()).connect();
+
+
+ // Send carrier config update for slot 0 only
+ setCarrierConfig(1 /*subId*/,
+ CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL,
+ true /*isEnabled*/);
+ setCarrierConfig(2 /*subId*/,
+ CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL,
+ false /*isEnabled*/);
+ sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
+ sendCarrierConfigChanged(1 /*slotId*/, 2 /*subId*/);
+ verify(mFeatureControllerSlot0).addFeature(mMockSipTransportSlot0,
+ SipTransportController.class);
+ verify(mFeatureControllerSlot1, never()).addFeature(mMockSipTransportSlot0,
+ SipTransportController.class);
+ verify(mFeatureControllerSlot0).connect();
+ verify(mFeatureControllerSlot1, never()).connect();
+ verify(mFeatureControllerSlot0).updateAssociatedSubscription(1);
+ verify(mFeatureControllerSlot1, never()).updateAssociatedSubscription(1);
+ }
+
+ @Test
public void testNoFeaturesEnabledCarrierConfigChanged() {
createRcsService(1 /*numSlots*/);
// No carrier config set for UCE.
sendCarrierConfigChanged(0, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
verify(mFeatureControllerSlot0, never()).addFeature(mMockUceSlot0,
- UserCapabilityExchangeImpl.class);
+ UceControllerManager.class);
verify(mFeatureControllerSlot0, never()).connect();
verify(mFeatureControllerSlot0, never()).updateAssociatedSubscription(anyInt());
}
@@ -122,27 +179,30 @@
@Test
public void testSlotUpdates() {
- setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, true /*isEnabled*/);
+ setCarrierConfig(1 /*subId*/, CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL,
+ true /*isEnabled*/);
+ setCarrierConfig(2 /*subId*/, CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL,
+ true /*isEnabled*/);
TelephonyRcsService service = createRcsService(1 /*numSlots*/);
- verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UceControllerManager.class);
verify(mFeatureControllerSlot0).connect();
// there should be no changes if the new num slots = old num
service.updateFeatureControllerSize(1 /*newNumSlots*/);
verify(mFeatureControllerSlot0, times(1)).addFeature(mMockUceSlot0,
- UserCapabilityExchangeImpl.class);
+ UceControllerManager.class);
verify(mFeatureControllerSlot0, times(1)).connect();
// Add a new slot.
verify(mFeatureControllerSlot1, never()).addFeature(mMockUceSlot1,
- UserCapabilityExchangeImpl.class);
+ UceControllerManager.class);
verify(mFeatureControllerSlot1, never()).connect();
service.updateFeatureControllerSize(2 /*newNumSlots*/);
// This shouldn't have changed for slot 0.
verify(mFeatureControllerSlot0, times(1)).addFeature(mMockUceSlot0,
- UserCapabilityExchangeImpl.class);
+ UceControllerManager.class);
verify(mFeatureControllerSlot0, times(1)).connect();
- verify(mFeatureControllerSlot1).addFeature(mMockUceSlot1, UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot1).addFeature(mMockUceSlot1, UceControllerManager.class);
verify(mFeatureControllerSlot1, times(1)).connect();
// Remove a slot.
@@ -151,10 +211,10 @@
service.updateFeatureControllerSize(1 /*newNumSlots*/);
// addFeature/connect shouldn't have been called again
verify(mFeatureControllerSlot0, times(1)).addFeature(mMockUceSlot0,
- UserCapabilityExchangeImpl.class);
+ UceControllerManager.class);
verify(mFeatureControllerSlot0, times(1)).connect();
verify(mFeatureControllerSlot1, times(1)).addFeature(mMockUceSlot1,
- UserCapabilityExchangeImpl.class);
+ UceControllerManager.class);
verify(mFeatureControllerSlot1, times(1)).connect();
// Verify destroy is only called for slot 1.
verify(mFeatureControllerSlot0, never()).destroy();
@@ -163,10 +223,13 @@
@Test
public void testCarrierConfigUpdate() {
- setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, true /*isEnabled*/);
+ setCarrierConfig(1 /*subId*/, CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL,
+ true /*isEnabled*/);
+ setCarrierConfig(2 /*subId*/, CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL,
+ true /*isEnabled*/);
createRcsService(2 /*numSlots*/);
- verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
- verify(mFeatureControllerSlot1).addFeature(mMockUceSlot1, UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UceControllerManager.class);
+ verify(mFeatureControllerSlot1).addFeature(mMockUceSlot1, UceControllerManager.class);
verify(mFeatureControllerSlot0).connect();
verify(mFeatureControllerSlot1).connect();
@@ -182,16 +245,38 @@
@Test
public void testCarrierConfigUpdateUceToNoUce() {
- setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, true /*isEnabled*/);
+ setCarrierConfig(1 /*subId*/, CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL,
+ true /*isEnabled*/);
createRcsService(1 /*numSlots*/);
- verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UceControllerManager.class);
verify(mFeatureControllerSlot0).connect();
// Send carrier config update for each slot.
- setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, false /*isEnabled*/);
+ setCarrierConfig(1 /*subId*/, CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL,
+ false /*isEnabled*/);
sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
- verify(mFeatureControllerSlot0).removeFeature(UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0).removeFeature(UceControllerManager.class);
+ verify(mFeatureControllerSlot0).updateAssociatedSubscription(1);
+ }
+
+ @Test
+ public void testCarrierConfigUpdateTransportToNoTransport() {
+ setCarrierConfig(1 /*subId*/,
+ CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL,
+ true /*isEnabled*/);
+ createRcsService(1 /*numSlots*/);
+ verify(mFeatureControllerSlot0).addFeature(mMockSipTransportSlot0,
+ SipTransportController.class);
+ verify(mFeatureControllerSlot0).connect();
+
+
+ // Send carrier config update for each slot.
+ setCarrierConfig(1 /*subId*/,
+ CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL,
+ false /*isEnabled*/);
+ sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
+ verify(mFeatureControllerSlot0).removeFeature(SipTransportController.class);
verify(mFeatureControllerSlot0).updateAssociatedSubscription(1);
}
@@ -199,14 +284,15 @@
public void testCarrierConfigUpdateNoUceToUce() {
createRcsService(1 /*numSlots*/);
verify(mFeatureControllerSlot0, never()).addFeature(mMockUceSlot0,
- UserCapabilityExchangeImpl.class);
+ UceControllerManager.class);
verify(mFeatureControllerSlot0, never()).connect();
// Send carrier config update for each slot.
- setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, true /*isEnabled*/);
+ setCarrierConfig(1 /*subId*/, CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL,
+ true /*isEnabled*/);
sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
- verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UceControllerManager.class);
verify(mFeatureControllerSlot0).connect();
verify(mFeatureControllerSlot0).updateAssociatedSubscription(1);
}
@@ -218,8 +304,8 @@
mReceiverCaptor.getValue().onReceive(mContext, intent);
}
- private void setCarrierConfig(String key, boolean value) {
- PersistableBundle bundle = mContext.getCarrierConfig();
+ private void setCarrierConfig(int subId, String key, boolean value) {
+ PersistableBundle bundle = mContext.getCarrierConfig(subId);
bundle.putBoolean(key, value);
}
diff --git a/tests/src/com/android/services/telephony/rcs/UceControllerManagerTest.java b/tests/src/com/android/services/telephony/rcs/UceControllerManagerTest.java
new file mode 100644
index 0000000..4148d13
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/UceControllerManagerTest.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2020 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 com.android.services.telephony.rcs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.net.Uri;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.TestExecutorService;
+import com.android.ims.RcsFeatureManager;
+import com.android.ims.rcs.uce.UceController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+
+@RunWith(AndroidJUnit4.class)
+public class UceControllerManagerTest extends TelephonyTestBase {
+
+ @Mock private UceController mUceController;
+ @Mock private RcsFeatureManager mRcsFeatureManager;
+
+ private final ExecutorService mExecutorService = new TestExecutorService();
+
+ private int mSlotId = 1;
+ private int mSubId = 1;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ doReturn(mSubId).when(mUceController).getSubId();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testRcsConnected() throws Exception {
+ UceControllerManager controllerManager = getUceControllerManager();
+
+ controllerManager.onRcsConnected(mRcsFeatureManager);
+
+ verify(mUceController).onRcsConnected(mRcsFeatureManager);
+ }
+
+ @Test
+ public void testRcsDisconnected() throws Exception {
+ UceControllerManager uceCtrlManager = getUceControllerManager();
+
+ uceCtrlManager.onRcsDisconnected();
+
+ verify(mUceController).onRcsDisconnected();
+ }
+
+ @Test
+ public void testDestroy() throws Exception {
+ UceControllerManager uceCtrlManager = getUceControllerManager();
+
+ uceCtrlManager.onDestroy();
+
+ verify(mUceController).onDestroy();
+ }
+
+ @Test
+ public void testSubscriptionUpdated() throws Exception {
+ UceControllerManager uceCtrlManager = getUceControllerManager();
+
+ uceCtrlManager.onAssociatedSubscriptionUpdated(mSubId);
+
+ verify(mUceController).onDestroy();
+ }
+
+ @Test
+ public void testRequestCapabilitiesWithRcsUnavailable() throws Exception {
+ UceControllerManager uceCtrlManager = getUceControllerManager();
+ doReturn(true).when(mUceController).isUnavailable();
+ uceCtrlManager.onRcsDisconnected();
+
+ try {
+ List<Uri> contacts = Arrays.asList(Uri.fromParts("sip", "00000", null));
+ IRcsUceControllerCallback callback = Mockito.mock(IRcsUceControllerCallback.class);
+
+ uceCtrlManager.requestCapabilities(contacts, callback);
+
+ fail();
+ } catch (ImsException e) {
+ assertEquals(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE, e.getCode());
+ return;
+ }
+ fail();
+ }
+
+ @Test
+ public void testRequestCapabilitiesWithRcsConnected() throws Exception {
+ UceControllerManager uceCtrlManager = getUceControllerManager();
+ doReturn(false).when(mUceController).isUnavailable();
+ uceCtrlManager.onRcsConnected(mRcsFeatureManager);
+
+ try {
+ List<Uri> contacts = Arrays.asList(Uri.fromParts("sip", "00000", null));
+ IRcsUceControllerCallback callback = Mockito.mock(IRcsUceControllerCallback.class);
+
+ uceCtrlManager.requestCapabilities(contacts, callback);
+
+ verify(mUceController).requestCapabilities(contacts, callback);
+ } catch (ImsException e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void testRequestNetworkAvailability() throws Exception {
+ UceControllerManager uceCtrlManager = getUceControllerManager();
+ doReturn(false).when(mUceController).isUnavailable();
+ uceCtrlManager.onRcsConnected(mRcsFeatureManager);
+
+ Uri contact = Uri.fromParts("sip", "00000", null);
+ IRcsUceControllerCallback callback = Mockito.mock(IRcsUceControllerCallback.class);
+
+ uceCtrlManager.requestNetworkAvailability(contact, callback);
+
+ verify(mUceController).requestAvailability(contact, callback);
+ }
+
+ @Test
+ public void testRequestNetworkAvailabilityWithRcsUnavailable() throws Exception {
+ UceControllerManager uceCtrlManager = getUceControllerManager();
+ doReturn(true).when(mUceController).isUnavailable();
+ uceCtrlManager.onRcsDisconnected();
+
+ try {
+ Uri contact = Uri.fromParts("sip", "00000", null);
+ IRcsUceControllerCallback callback = Mockito.mock(IRcsUceControllerCallback.class);
+ uceCtrlManager.requestNetworkAvailability(contact, callback);
+ fail();
+ } catch (ImsException e) {
+ assertEquals(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE, e.getCode());
+ return;
+ }
+ fail();
+ }
+
+ @Test
+ public void testGetPublishState() throws Exception {
+ UceControllerManager uceCtrlManager = getUceControllerManager();
+ doReturn(false).when(mUceController).isUnavailable();
+ uceCtrlManager.onRcsConnected(mRcsFeatureManager);
+
+ uceCtrlManager.getUcePublishState();
+
+ verify(mUceController).getUcePublishState();
+ }
+
+ @Test
+ public void testGetPublishStateWithRcsUnavailable() throws Exception {
+ UceControllerManager uceCtrlManager = getUceControllerManager();
+ doReturn(true).when(mUceController).isUnavailable();
+ uceCtrlManager.onRcsDisconnected();
+
+ try {
+ uceCtrlManager.getUcePublishState();
+ fail();
+ } catch (ImsException e) {
+ assertEquals(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE, e.getCode());
+ return;
+ }
+ fail();
+ }
+
+ @Test
+ public void testRegisterPublishStateCallback() throws Exception {
+ UceControllerManager uceCtrlManager = getUceControllerManager();
+ IRcsUcePublishStateCallback callback = Mockito.mock(IRcsUcePublishStateCallback.class);
+
+ uceCtrlManager.registerPublishStateCallback(callback);
+
+ verify(mUceController).registerPublishStateCallback(callback);
+ }
+
+ @Test
+ public void testRegisterPublishStateCallbackWithRcsUnavailable() throws Exception {
+ UceControllerManager uceCtrlManager = getUceControllerManager();
+ doReturn(true).when(mUceController).isUnavailable();
+ uceCtrlManager.onRcsDisconnected();
+
+ try {
+ IRcsUcePublishStateCallback callback = Mockito.mock(IRcsUcePublishStateCallback.class);
+ uceCtrlManager.registerPublishStateCallback(callback);
+ fail();
+ } catch (ImsException e) {
+ assertEquals(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE, e.getCode());
+ return;
+ }
+ fail();
+ }
+
+ @Test
+ public void testUnregisterPublishStateCallback() throws Exception {
+ UceControllerManager uceCtrlManager = getUceControllerManager();
+ IRcsUcePublishStateCallback callback = Mockito.mock(IRcsUcePublishStateCallback.class);
+
+ uceCtrlManager.unregisterPublishStateCallback(callback);
+
+ verify(mUceController).unregisterPublishStateCallback(callback);
+ }
+
+ private UceControllerManager getUceControllerManager() {
+ UceControllerManager manager = new UceControllerManager(mContext, mSlotId, mSubId,
+ mExecutorService);
+ manager.setUceController(mUceController);
+ return manager;
+ }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/UserCapabilityExchangeImplTest.java b/tests/src/com/android/services/telephony/rcs/UserCapabilityExchangeImplTest.java
deleted file mode 100644
index 82ecd79..0000000
--- a/tests/src/com/android/services/telephony/rcs/UserCapabilityExchangeImplTest.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.services.telephony.rcs;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyList;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.net.Uri;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.RemoteCallbackList;
-import android.telephony.ims.ImsManager;
-import android.telephony.ims.ImsMmTelManager;
-import android.telephony.ims.RcsContactUceCapability;
-import android.telephony.ims.RegistrationManager;
-import android.telephony.ims.aidl.IRcsUceControllerCallback;
-import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
-import android.telephony.ims.stub.RcsCapabilityExchange;
-import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.TelephonyTestBase;
-import com.android.ims.RcsFeatureManager;
-import com.android.ims.RcsFeatureManager.RcsFeatureCallbacks;
-import com.android.ims.ResultCode;
-import com.android.service.ims.presence.PresenceBase;
-import com.android.service.ims.presence.PresencePublication;
-import com.android.service.ims.presence.PresencePublisher;
-import com.android.service.ims.presence.PresenceSubscriber;
-import com.android.service.ims.presence.SubscribePublisher;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-@RunWith(AndroidJUnit4.class)
-public class UserCapabilityExchangeImplTest extends TelephonyTestBase {
-
- private int mSlotId = 0;
- private int mSubId = 1;
- private int mUpdatedSubId = 2;
-
- @Captor ArgumentCaptor<IRcsUcePublishStateCallback> mPublishStateCallbacksCaptor;
-
- @Mock PresencePublication mPresencePublication;
- @Mock PresenceSubscriber mPresenceSubscriber;
- @Mock RcsFeatureManager mRcsFeatureManager;
- @Mock ImsMmTelManager mImsMmTelManager;
- @Mock RemoteCallbackList<IRcsUcePublishStateCallback> mPublishStateCallbacks;
-
- private Looper mLooper;
-
- @Before
- public void setUp() throws Exception {
- super.setUp();
-
- ImsManager imsManager =
- (ImsManager) mContext.getSystemService(Context.TELEPHONY_IMS_SERVICE);
- when(imsManager.getImsMmTelManager(mSubId)).thenReturn(mImsMmTelManager);
- }
-
- @After
- public void tearDown() throws Exception {
- super.tearDown();
-
- if (mLooper != null) {
- mLooper.quit();
- mLooper = null;
- }
- }
-
- @Test
- public void testServiceConnected() throws Exception {
- UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
- uceImpl.onRcsConnected(mRcsFeatureManager);
-
- verify(mRcsFeatureManager).addFeatureListenerCallback(any(RcsFeatureCallbacks.class));
- verify(mPresencePublication).updatePresencePublisher(any(PresencePublisher.class));
- verify(mPresenceSubscriber).updatePresenceSubscriber(any(SubscribePublisher.class));
- }
-
- @Test
- public void testServiceDisconnected() throws Exception {
- UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
- uceImpl.onRcsDisconnected();
-
- verify(mPresencePublication).removePresencePublisher();
- verify(mPresenceSubscriber).removePresenceSubscriber();
- }
-
- @Test
- public void testSubscriptionUpdated() throws Exception {
- UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
- uceImpl.onAssociatedSubscriptionUpdated(mUpdatedSubId);
-
- verify(mImsMmTelManager).registerImsRegistrationCallback(any(Executor.class),
- any(RegistrationManager.RegistrationCallback.class));
- verify(mImsMmTelManager).registerMmTelCapabilityCallback(any(Executor.class),
- any(ImsMmTelManager.CapabilityCallback.class));
- verify(mPresencePublication).handleAssociatedSubscriptionChanged(mUpdatedSubId);
- verify(mPresenceSubscriber).handleAssociatedSubscriptionChanged(mUpdatedSubId);
- }
-
- @Test
- public void testUcePublishStateRetrieval() throws Exception {
- UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
- uceImpl.getUcePublishState();
-
- verify(mPresencePublication).getPublishState();
- }
-
- @Test
- public void testRegisterPublishStateCallbacks() throws Exception {
- UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
- uceImpl.registerPublishStateCallback(any(IRcsUcePublishStateCallback.class));
- verify(mPublishStateCallbacks).register(mPublishStateCallbacksCaptor.capture());
- }
-
- @Test
- public void testOnNotifyUpdateCapabilities() throws Exception {
- UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
- uceImpl.onRcsConnected(mRcsFeatureManager);
-
- int triggerType = RcsPresenceExchangeImplBase.CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN;
- uceImpl.mRcsFeatureCallback.onNotifyUpdateCapabilities(triggerType);
- waitForMs(1000);
-
- verify(mPresencePublication).onStackPublishRequested(triggerType);
- }
-
- @Test
- public void testRequestPublicationWithSuccessfulResponse() throws Exception {
- int taskId = 1;
- int sipResponse = 200;
- Uri contact = Uri.fromParts("sip", "test", null);
- RcsContactUceCapability.Builder builder = new RcsContactUceCapability.Builder(contact);
- RcsContactUceCapability capability = builder.build();
-
- UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
- uceImpl.onRcsConnected(mRcsFeatureManager);
-
- doAnswer(invocation -> {
- uceImpl.mRcsFeatureCallback.onCommandUpdate(RcsCapabilityExchange.COMMAND_CODE_SUCCESS,
- taskId);
- uceImpl.mRcsFeatureCallback.onNetworkResponse(sipResponse, null, taskId);
- return null;
- }).when(mRcsFeatureManager).requestPublication(capability, taskId);
-
- // Request publication
- int result = uceImpl.requestPublication(capability, contact.toString(), taskId);
-
- assertEquals(ResultCode.SUCCESS, result);
- verify(mPresencePublication).onCommandStatusUpdated(taskId, taskId, ResultCode.SUCCESS);
- verify(mPresencePublication).onSipResponse(taskId, sipResponse, null);
- }
-
- @Test
- public void testRequestPublicationWithFailedResponse() throws Exception {
- int taskId = 1;
- Uri contact = Uri.fromParts("sip", "test", null);
- RcsContactUceCapability.Builder builder = new RcsContactUceCapability.Builder(contact);
- RcsContactUceCapability capability = builder.build();
-
- UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
- uceImpl.onRcsConnected(mRcsFeatureManager);
-
- doAnswer(invocation -> {
- uceImpl.mRcsFeatureCallback.onCommandUpdate(
- RcsCapabilityExchange.COMMAND_CODE_GENERIC_FAILURE, taskId);
- return null;
- }).when(mRcsFeatureManager).requestPublication(capability, taskId);
-
- // Request publication
- int result = uceImpl.requestPublication(capability, contact.toString(), taskId);
-
- assertEquals(ResultCode.SUCCESS, result);
- verify(mPresencePublication).onCommandStatusUpdated(taskId, taskId,
- ResultCode.PUBLISH_GENERIC_FAILURE);
- }
-
- @Test
- public void testRequestCapability() throws Exception {
- int taskId = 1;
- int sipResponse = 200;
- List<RcsContactUceCapability> infos = new ArrayList<>();
- List<Uri> contacts = Arrays.asList(Uri.fromParts("sip", "00000", null));
- IRcsUceControllerCallback callback = Mockito.mock(IRcsUceControllerCallback.class);
-
- UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
- uceImpl.onRcsConnected(mRcsFeatureManager);
-
- when(mPresenceSubscriber.requestCapability(anyList(), any())).thenReturn(taskId);
-
- doAnswer(invocation -> {
- uceImpl.mRcsFeatureCallback.onCommandUpdate(RcsCapabilityExchange.COMMAND_CODE_SUCCESS,
- taskId);
- uceImpl.mRcsFeatureCallback.onNetworkResponse(sipResponse, null, taskId);
- uceImpl.mRcsFeatureCallback.onCapabilityRequestResponsePresence(infos, taskId);
- return null;
- }).when(mRcsFeatureManager).requestCapabilities(anyList(), anyInt());
-
- uceImpl.requestCapabilities(contacts, callback);
- uceImpl.requestCapability(new String[] {"00000"}, taskId);
-
- verify(mPresenceSubscriber).onCommandStatusUpdated(taskId, taskId, ResultCode.SUCCESS);
- verify(mPresenceSubscriber).onSipResponse(taskId, sipResponse, null);
- verify(mPresenceSubscriber).updatePresences(taskId, infos, true, null);
- }
-
- @Test
- public void testUpdatePublisherState() throws Exception {
- IRcsUcePublishStateCallback callback = Mockito.mock(IRcsUcePublishStateCallback.class);
- doAnswer(invocation -> {
- callback.onPublishStateChanged(anyInt());
- return null;
- }).when(mPublishStateCallbacks).broadcast(any());
-
- UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
- uceImpl.onRcsConnected(mRcsFeatureManager);
- uceImpl.registerPublishStateCallback(callback);
- uceImpl.updatePublisherState(PresenceBase.PUBLISH_STATE_200_OK);
-
- assertEquals(PresenceBase.PUBLISH_STATE_200_OK, uceImpl.getPublisherState());
- verify(callback).onPublishStateChanged(anyInt());
- }
-
- @Test
- public void testUnpublish() throws Exception {
- IRcsUcePublishStateCallback callback = Mockito.mock(IRcsUcePublishStateCallback.class);
- doAnswer(invocation -> {
- callback.onPublishStateChanged(anyInt());
- return null;
- }).when(mPublishStateCallbacks).broadcast(any());
-
- UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
- uceImpl.onRcsConnected(mRcsFeatureManager);
- uceImpl.mRcsFeatureCallback.onUnpublish();
- waitForMs(1000);
-
- verify(mPresencePublication).setPublishState(PresenceBase.PUBLISH_STATE_NOT_PUBLISHED);
- }
-
- private UserCapabilityExchangeImpl createUserCapabilityExchangeImpl() throws Exception {
- HandlerThread handlerThread = new HandlerThread("UceImplHandlerThread");
- handlerThread.start();
- mLooper = handlerThread.getLooper();
- UserCapabilityExchangeImpl uceImpl = new UserCapabilityExchangeImpl(mContext, mSlotId,
- mSubId, mLooper, mPresencePublication, mPresenceSubscriber,
- mPublishStateCallbacks);
- verify(mPresencePublication).handleAssociatedSubscriptionChanged(1);
- verify(mPresenceSubscriber).handleAssociatedSubscriptionChanged(1);
- waitForHandlerAction(uceImpl.getHandler(), 1000);
- verify(mImsMmTelManager, atLeast(1)).registerImsRegistrationCallback(
- any(Executor.class), any(RegistrationManager.RegistrationCallback.class));
- verify(mContext).registerReceiver(any(), any());
- return uceImpl;
- }
-}