Merge "Add RCS metrics of sipDelegate/sipDelegateFeatureTags"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index da61c80..3831b6b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -12,14 +12,6 @@
"name": "CarrierAppIntegrationTestCases"
},
{
- "name": "CtsTelephonySdk28TestCases",
- "options": [
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
- },
- {
"name": "CtsSimRestrictedApisTestCases",
"options": [
{
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 43f4d95..8c257f3 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -534,7 +534,7 @@
<string name="notification_voicemail_no_vm_number" msgid="3423686009815186750">"ভইচমেইলৰ নম্বৰ অজ্ঞাত"</string>
<string name="notification_network_selection_title" msgid="255595526707809121">"কোনো সেৱা নাই"</string>
<string name="notification_network_selection_text" msgid="553288408722427659">"বাছনি কৰা (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) নেটৱৰ্কটো উপলব্ধ নহয়"</string>
- <string name="incall_error_power_off" product="watch" msgid="7191184639454113633">"কল কৰিবৰ কাৰণে ম’বাইল নেটৱৰ্ক অন কৰক, এয়াৰপ্লেইন ম\'ড অফ কৰক বা বেটাৰি সঞ্চয়কাৰী ম\'ড অফ কৰক।"</string>
+ <string name="incall_error_power_off" product="watch" msgid="7191184639454113633">"কল কৰিবৰ কাৰণে ম’বাইল নেটৱৰ্ক অন কৰক, এয়াৰপ্লেইন ম’ড অফ কৰক বা বেটাৰী সঞ্চয়কাৰী ম’ড অফ কৰক।"</string>
<string name="incall_error_power_off" product="default" msgid="8131672264311208673">"কল কৰিবৰ কাৰণে এয়াৰপ্লেইন ম\'ড অফ কৰক।"</string>
<string name="incall_error_power_off_wfc" msgid="9125661184694727052">"কল কৰিবৰ কাৰণে এয়াৰপ্লেইন ম\'ড অফ কৰক বা কোনো বেতাঁৰ নেটৱৰ্কৰ সৈতে সংযোগ কৰক।"</string>
<string name="incall_error_power_off_thermal" product="default" msgid="8695809601655300168"><b>"ফ’নটো বেছি গৰম হৈছে"</b>\n\n"এই কলটো সম্পূৰ্ণ কৰিব নোৱাৰি। আপোনাৰ ফ’নটো ঠাণ্ডা হোৱাৰ পাছত পুনৰ চেষ্টা কৰক।\n\nআপুনি তথাপি জৰুৰীকালীন কল কৰিব পাৰিব।"</string>
@@ -689,8 +689,8 @@
<string name="callFailed_cdma_call_limit" msgid="1074219746093031412">"কেইবাটাও কল ইতিমধ্যে সক্ৰিয় হৈ আছে। নতুন কল এটা কৰাৰ আগেয়ে অনুগ্ৰহ কৰি সেইবোৰ একেলগ কৰক বা সমাপ্ত কৰক।"</string>
<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="dialFailed_low_battery" msgid="6857904237423407056">"বেটাৰী কম থকাৰ বাবে ভিডিঅ’ কল কৰিব নোৱাৰি।"</string>
+ <string name="callFailed_low_battery" msgid="4056828320214416182">"বেটাৰী কম থকাৰ বাবে ভিডিঅ’ কল সমাপ্ত হৈছে।"</string>
<string name="callFailed_emergency_call_over_wfc_not_available" msgid="5944309590693432042">"এই অৱস্থানটোত ৱাই-ফাই কলিঙৰ জৰিয়তে জৰুৰীকালীন কল কৰাৰ সুবিধা উপলব্ধ নহয়।"</string>
<string name="callFailed_wfc_service_not_available_in_this_location" msgid="3624536608369524988">"এই অৱস্থানটোত ৱাই-ফাই কলিং উপলব্ধ নহয়।"</string>
<string name="change_pin_title" msgid="3564254326626797321">"ভইচমেইলৰ পিন সলনি কৰক"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index a5c5e86..7738bcd 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -310,7 +310,7 @@
<string name="sim_change_data_title" msgid="9142726786345906606">"Promijeniti SIM za prijenos podataka?"</string>
<string name="sim_change_data_message" msgid="3567358694255933280">"Koristiti SIM karticu <xliff:g id="NEW_SIM">%1$s</xliff:g> umjesto SIM kartice <xliff:g id="OLD_SIM">%2$s</xliff:g> za prijenos podataka na mobilnoj mreži?"</string>
<string name="wifi_calling_settings_title" msgid="5800018845662016507">"Pozivanje putem WiFi-ja"</string>
- <string name="video_calling_settings_title" msgid="342829454913266078">"Operater video pozivanja"</string>
+ <string name="video_calling_settings_title" msgid="342829454913266078">"Video pozivi putem operatera"</string>
<string name="gsm_umts_options" msgid="4968446771519376808">"GSM/UMTS opcije"</string>
<string name="cdma_options" msgid="3669592472226145665">"CDMA opcije"</string>
<string name="throttle_data_usage" msgid="1944145350660420711">"Korištenje podataka"</string>
@@ -839,7 +839,7 @@
<string name="radio_info_data_connection_enable" msgid="6183729739783252840">"Omogućite vezu za prijenos podataka"</string>
<string name="radio_info_data_connection_disable" msgid="6404751291511368706">"Onemogući vezu za prijenos podataka"</string>
<string name="volte_provisioned_switch_string" msgid="4812874990480336178">"VoLTE omogućen"</string>
- <string name="vt_provisioned_switch_string" msgid="8295542122512195979">"Video poziv obezbijeđen"</string>
+ <string name="vt_provisioned_switch_string" msgid="8295542122512195979">"Video pozivi su omogućeni"</string>
<string name="wfc_provisioned_switch_string" msgid="3835004640321078988">"WiFi poziv obezbijeđen"</string>
<string name="eab_provisioned_switch_string" msgid="4449676720736033035">"EAB/Omogućeno prisustvo"</string>
<string name="cbrs_data_switch_string" msgid="6060356430838077653">"Cbrs podaci"</string>
@@ -858,7 +858,7 @@
<string name="radio_info_ims_reg_status_not_registered" msgid="8045821447288876085">"Nije registrirano"</string>
<string name="radio_info_ims_feature_status_available" msgid="6493200914756969292">"Dostupno"</string>
<string name="radio_info_ims_feature_status_unavailable" msgid="8930391136839759778">"Nedostupno"</string>
- <string name="radio_info_ims_reg_status" msgid="25582845222446390">"IMS registracija: <xliff:g id="STATUS">%1$s</xliff:g>\nGovor putem LTE mreže: <xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nGovor putem WiFi mreže: <xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\nVideo pozivanje: <xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nUT interfejs: <xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
+ <string name="radio_info_ims_reg_status" msgid="25582845222446390">"IMS registracija: <xliff:g id="STATUS">%1$s</xliff:g>\nGovor putem LTE mreže: <xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nGovor putem WiFi mreže: <xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\nVideo pozivi: <xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nUT interfejs: <xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
<string name="radioInfo_service_in" msgid="45753418231446400">"Aktivno"</string>
<string name="radioInfo_service_out" msgid="287972405416142312">"Ne radi"</string>
<string name="radioInfo_service_emergency" msgid="4763879891415016848">"Samo hitni pozivi"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index b2a4e2e..5e5a3c5 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -63,18 +63,18 @@
<string name="labelCdmaMore_with_label" msgid="7759692829160238152">"CDMA қоңырау параметрлері (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
<string name="apn_settings" msgid="1978652203074756623">"APN"</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>
<string name="phone_accounts_default_account_label" msgid="5107598881335931101">"Қолжетімді желі жоқ"</string>
<string name="phone_accounts_settings_header" msgid="6296501692964706536">"Параметрлер"</string>
- <string name="phone_accounts_choose_accounts" msgid="4748805293314824974">"Есептік жазбаларды таңдау"</string>
- <string name="phone_accounts_selection_header" msgid="2945830843104108440">"Телефон есептік жазбалары"</string>
- <string name="phone_accounts_add_sip_account" msgid="1437634802033309305">"SIP есептік жазбасын қосу"</string>
- <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="phone_accounts_choose_accounts" msgid="4748805293314824974">"Аккаунттарды таңдау"</string>
+ <string name="phone_accounts_selection_header" msgid="2945830843104108440">"Телефон аккаунттары"</string>
+ <string name="phone_accounts_add_sip_account" msgid="1437634802033309305">"SIP аккаунтын қосу"</string>
+ <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="connection_service_default_label" msgid="7332739049855715584">"Кірістірілген байланыс қызметі"</string>
<string name="voicemail" msgid="7697769412804195032">"Дауыстық пошта"</string>
@@ -294,8 +294,8 @@
<string name="limited_sim_function_notification_title" msgid="612715399099846281">"SIM картасының жұмысы шектеулі"</string>
<string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> нөмірін пайдаланған кезде <xliff:g id="CARRIER_NAME">%1$s</xliff:g> қоңыраулары мен дерек тасымалдау қызметтері бөгелуі мүмкін."</string>
<string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Екінші SIM картасын пайдаланылғанда, <xliff:g id="CARRIER_NAME">%1$s</xliff:g> қоңырауы мен дерек қызметі бөгелуі мүмкін."</string>
- <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Қолданыстан шыққан SIP есептік жазбалары табылды және өшірілді"</string>
- <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android платформасында бұдан былай SIP арқылы қоңырау шалу мүмкін емес.\nҚазіргі SIP есептік жазбаларыңыз (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) өшірілді.\nҚоңырау шалуға қолданылатын әдепкі есептік жазба параметрін растаңыз."</string>
+ <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Қолданыстан шыққан SIP аккаунттары табылды және өшірілді"</string>
+ <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android платформасында бұдан былай SIP арқылы қоңырау шалу мүмкін емес.\nҚазіргі SIP аккаунтларыңыз (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) өшірілді.\nҚоңырау шалуға қолданылатын әдепкі аккаунт параметрін растаңыз."</string>
<string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Параметрлерге өту"</string>
<string name="data_usage_title" msgid="8438592133893837464">"Қолданба деректерінің трафигі"</string>
<string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_2">%2$s</xliff:g> аралығында <xliff:g id="ID_1">%1$s</xliff:g> мобильдік дерек қолданылды"</string>
@@ -926,5 +926,5 @@
<string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Операторды инциализациялауды іске қосу"</string>
<string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth сигналы нашар. Спикерфонға ауысып көріңіз."</string>
<string name="call_quality_notification_name" msgid="3476828289553948830">"Қоңырау сапасы туралы хабарландыру"</string>
- <string name="notification_channel_sip_account" msgid="1261816025156179637">"Қолданыстан шыққан SIP есептік жазбалары"</string>
+ <string name="notification_channel_sip_account" msgid="1261816025156179637">"Қолданыстан шыққан SIP аккаунттары"</string>
</resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 8ec61fa..3e76079 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -340,22 +340,22 @@
<string name="maintenance_disable" msgid="2121032601497725602">"അറ്റകുറ്റപ്പണി സേവനങ്ങൾ പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="general_news_settings" msgid="2670499575962080411">"പൊതുവായ വാർത്തകൾ"</string>
<string name="bf_news_settings" msgid="8571709425370794221">"ബിസിനസ്സ്, സാമ്പത്തിക വാർത്തകൾ"</string>
- <string name="sports_news_settings" msgid="2684364556989168438">"കായിക വാർത്ത"</string>
- <string name="entertainment_news_settings" msgid="4228527702346305543">"വിനോദവാർത്ത"</string>
+ <string name="sports_news_settings" msgid="2684364556989168438">"കായിക വാർത്തകൾ"</string>
+ <string name="entertainment_news_settings" msgid="4228527702346305543">"വിനോദ വാർത്തകൾ"</string>
<string name="enable_disable_local" msgid="7654175079979415572">"പ്രാദേശികം"</string>
- <string name="local_enable" msgid="790606890868710629">"പ്രാദേശിക വാർത്ത പ്രവർത്തനക്ഷമമാക്കി"</string>
- <string name="local_disable" msgid="7649945293198602877">"പ്രാദേശിക വാർത്ത പ്രവർത്തനരഹിതമാക്കി"</string>
+ <string name="local_enable" msgid="790606890868710629">"പ്രാദേശിക വാർത്തകൾ പ്രവർത്തനക്ഷമമാക്കി"</string>
+ <string name="local_disable" msgid="7649945293198602877">"പ്രാദേശിക വാർത്തകൾ പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="enable_disable_regional" msgid="5783403191376564638">"പ്രാദേശികം"</string>
<string name="regional_enable" msgid="7730109417536296079">"പ്രാദേശിക വാർത്തകൾ പ്രവർത്തനക്ഷമമാക്കി"</string>
<string name="regional_disable" msgid="3781951818157772545">"പ്രാദേശിക വാർത്തകൾ പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="enable_disable_national" msgid="6198481711934897632">"ദേശീയം"</string>
- <string name="national_enable" msgid="5159683504138239304">"ദേശീയ വാർത്ത പ്രവർത്തനക്ഷമമാക്കി"</string>
- <string name="national_disable" msgid="8484356368757118987">"ദേശീയ വാർത്ത പ്രവർത്തനരഹിതമാക്കി"</string>
+ <string name="national_enable" msgid="5159683504138239304">"ദേശീയ വാർത്തകൾ പ്രവർത്തനക്ഷമമാക്കി"</string>
+ <string name="national_disable" msgid="8484356368757118987">"ദേശീയ വാർത്തകൾ പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="enable_disable_international" msgid="4204334217211198792">"അന്തര്ദ്ദേശീയം"</string>
- <string name="international_enable" msgid="8943466745792690340">"അന്തർദ്ദേശീയ വാർത്ത പ്രവർത്തനക്ഷമമാക്കി"</string>
+ <string name="international_enable" msgid="8943466745792690340">"അന്തർദ്ദേശീയ വാർത്തകൾ പ്രവർത്തനക്ഷമമാക്കി"</string>
<string name="international_disable" msgid="4803498658100318265">"അന്തർദ്ദേശീയ വാർത്തകൾ പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="list_language_title" msgid="1850167908665485738">"ഭാഷ"</string>
- <string name="list_language_summary" msgid="7921756070782277559">"വാർത്താഭാഷ തിരഞ്ഞെടുക്"</string>
+ <string name="list_language_summary" msgid="7921756070782277559">"വാർത്താഭാഷ തിരഞ്ഞെടുക്കുക"</string>
<string-array name="list_language_entries">
<item msgid="2347238508726934281">"ഇംഗ്ലീഷ്"</item>
<item msgid="5172468397620875174">"ഫ്രഞ്ച്"</item>
@@ -405,9 +405,9 @@
<string name="enable_disable_mhh" msgid="715930476289202466">"മെഡിക്കൽ, ആരോഗ്യം, ആശുപത്രി എന്നിവ"</string>
<string name="mhh_enable" msgid="7224396815285147008">"മെഡിക്കൽ, ആരോഗ്യം, ആശുപത്രി എന്നിവ പ്രവർത്തനക്ഷമമാക്കി"</string>
<string name="mhh_disable" msgid="5503643028885686265">"മെഡിക്കൽ, ആരോഗ്യം, ആശുപത്രി എന്നിവ പ്രവർത്തനരഹിതമാക്കി"</string>
- <string name="enable_disable_technology_news" msgid="2794845609698078400">"സാങ്കേതിക വാർത്ത"</string>
- <string name="technology_news_enable" msgid="1908991199492598311">"സാങ്കേതിക വാർത്ത പ്രവർത്തനക്ഷമമാക്കി"</string>
- <string name="technology_news_disable" msgid="8388582607149800889">"സാങ്കേതിക വാർത്ത പ്രവർത്തനരഹിതമാക്കി"</string>
+ <string name="enable_disable_technology_news" msgid="2794845609698078400">"സാങ്കേതിക വാർത്തകൾ"</string>
+ <string name="technology_news_enable" msgid="1908991199492598311">"സാങ്കേതിക വാർത്തകൾ പ്രവർത്തനക്ഷമമാക്കി"</string>
+ <string name="technology_news_disable" msgid="8388582607149800889">"സാങ്കേതിക വാർത്തകൾ പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="enable_disable_multi_category" msgid="5958248155437940625">"ഒന്നിലധികം വിഭാഗം"</string>
<string name="multi_category_enable" msgid="4531915767817483960">"ഒന്നിലധികം വിഭാഗം പ്രവർത്തനക്ഷമമാക്കി"</string>
<string name="multi_category_disable" msgid="6325934413701238104">"ഒന്നിലധികം വിഭാഗം പ്രവർത്തനരഹിതമാക്കി"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 142d1e1..e16c4fa 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -596,7 +596,7 @@
<string name="hac_mode_summary" msgid="7774989500136009881">"Slå på kompatibilitet med høreapparater"</string>
<string name="rtt_mode_title" msgid="3075948111362818043">"Sanntidstekst-anrop (STT)"</string>
<string name="rtt_mode_summary" msgid="8631541375609989562">"Tillat meldingsutveksling i talesamtaler"</string>
- <string name="rtt_mode_more_information" msgid="587500128658756318">"STT er til hjelp for brukere som er døve, tunghørte, har talefunksjonshemning eller trenger mer enn bare tale.<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>Finn ut mer</a>\n <br><br> – STT-anrop lagres som meldingstranskripsjoner\n <br> – STT er ikke tilgjengelig for videoanrop"</string>
+ <string name="rtt_mode_more_information" msgid="587500128658756318">"STT er til hjelp for brukere som er døve, hørselshemmede, har taleproblemer eller trenger mer enn bare tale.<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>Finn ut mer</a>\n <br><br> – STT-anrop lagres som meldingstranskripsjoner\n <br> – STT er ikke tilgjengelig for videoanrop"</string>
<string name="no_rtt_when_roaming" msgid="5268008247378355389">"Merk: RTT er ikke tilgjengelig ved roaming"</string>
<string-array name="tty_mode_entries">
<item msgid="3238070884803849303">"TTY av"</item>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 6ff98c6..850bfbb 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -125,9 +125,9 @@
<string name="sum_cfnrc_disabled" msgid="739289696796917683">"Uit"</string>
<string name="disable_cfnrc_forbidden" msgid="775348748084726890">"Je provider biedt geen ondersteuning voor het uitschakelen van oproepdoorschakelingen wanneer je telefoon niet bereikbaar is."</string>
<string name="registration_cf_forbidden" msgid="4386482610771190420">"Je provider ondersteunt het doorschakelen van gesprekken niet."</string>
- <string name="cdma_call_waiting" msgid="4565070960879673216">"Wisselgesprek inschakelen?"</string>
+ <string name="cdma_call_waiting" msgid="4565070960879673216">"Wisselgesprek aanzetten?"</string>
<string name="enable_cdma_call_waiting_setting" msgid="5906811747921744307">"Tijdens een gesprek krijg je een melding over inkomende gesprekken"</string>
- <string name="enable_cdma_cw" msgid="811047045863422232">"Inschakelen"</string>
+ <string name="enable_cdma_cw" msgid="811047045863422232">"Aanzetten"</string>
<string name="disable_cdma_cw" msgid="7119290446496301734">"Annuleren"</string>
<string name="cdma_call_waiting_in_ims_on" msgid="6390979414188659218">"CDMA-wisselgesprek onder IMS aan"</string>
<string name="cdma_call_waiting_in_ims_off" msgid="1099246114368636334">"CDMA-wisselgesprek onder IMS uit"</string>
@@ -147,7 +147,7 @@
<string name="fdn_check_failure" msgid="1833769746374185247">"De instelling \'Vaste nummers\' in je app Telefoon is ingeschakeld. Hierdoor werken sommige oproepgerelateerde functies niet."</string>
<string name="radio_off_error" msgid="8321564164914232181">"Schakel de radio in voordat je deze instellingen bekijkt."</string>
<string name="close_dialog" msgid="1074977476136119408">"OK"</string>
- <string name="enable" msgid="2636552299455477603">"Inschakelen"</string>
+ <string name="enable" msgid="2636552299455477603">"Aanzetten"</string>
<string name="disable" msgid="1122698860799462116">"Uit"</string>
<string name="change_num" msgid="6982164494063109334">"Updaten"</string>
<string-array name="clir_display_values">
@@ -443,20 +443,20 @@
<string name="fdn_activation" msgid="2178637004710435895">"FDN-activering"</string>
<string name="fdn_enabled" msgid="7017355494808056447">"Vaste nummers zijn ingeschakeld"</string>
<string name="fdn_disabled" msgid="6696468878037736600">"Vaste nummers zijn uitgeschakeld"</string>
- <string name="enable_fdn" msgid="4830555730418033723">"FDN inschakelen"</string>
+ <string name="enable_fdn" msgid="4830555730418033723">"FDN aanzetten"</string>
<string name="disable_fdn" msgid="3918794950264647541">"FDN uitschakelen"</string>
<string name="change_pin2" msgid="3110844547237754871">"PIN2-code wijzigen"</string>
<string name="enable_fdn_ok" msgid="5080925177369329827">"FDN uitschakelen"</string>
- <string name="disable_fdn_ok" msgid="3745475926874838676">"FDN inschakelen"</string>
+ <string name="disable_fdn_ok" msgid="3745475926874838676">"FDN aanzetten"</string>
<string name="sum_fdn" msgid="6152246141642323582">"Vaste nummers beheren"</string>
<string name="sum_fdn_change_pin" msgid="3510994280557335727">"Pincode voor FDN-toegang wijzigen"</string>
<string name="sum_fdn_manage_list" msgid="3311397063233992907">"Lijst met telefoonnummers beheren"</string>
<string name="voice_privacy" msgid="7346935172372181951">"Spraakprivacy"</string>
- <string name="voice_privacy_summary" msgid="3556460926168473346">"Geavanceerde privacymodus inschakelen"</string>
+ <string name="voice_privacy_summary" msgid="3556460926168473346">"Geavanceerde privacymodus aanzetten"</string>
<string name="tty_mode_option_title" msgid="3843817710032641703">"TTY-modus"</string>
<string name="tty_mode_option_summary" msgid="4770510287236494371">"TTY-modus instellen"</string>
<string name="auto_retry_mode_title" msgid="2985801935424422340">"Automatisch opnieuw proberen"</string>
- <string name="auto_retry_mode_summary" msgid="2863919925349511402">"Modus voor automatisch opnieuw proberen inschakelen"</string>
+ <string name="auto_retry_mode_summary" msgid="2863919925349511402">"Modus voor automatisch opnieuw proberen aanzetten"</string>
<string name="tty_mode_not_allowed_video_call" msgid="6551976083652752815">"De TTY-modus mag niet worden gewijzigd tijdens een videogesprek"</string>
<string name="menu_add" msgid="5616487894975773141">"Contact toevoegen"</string>
<string name="menu_edit" msgid="3593856941552460706">"Contact bewerken"</string>
@@ -593,7 +593,7 @@
<string name="singleContactImportedMsg" msgid="3619804066300998934">"Contact geïmporteerd"</string>
<string name="failedToImportSingleContactMsg" msgid="228095510489830266">"Kan contact niet importeren"</string>
<string name="hac_mode_title" msgid="4127986689621125468">"Hoortoestellen"</string>
- <string name="hac_mode_summary" msgid="7774989500136009881">"Compatibiliteit voor hoortoestel inschakelen"</string>
+ <string name="hac_mode_summary" msgid="7774989500136009881">"Compatibiliteit voor hoortoestel aanzetten"</string>
<string name="rtt_mode_title" msgid="3075948111362818043">"Realtime tekstoproep (RTT)"</string>
<string name="rtt_mode_summary" msgid="8631541375609989562">"Berichten in een audiogesprek toestaan"</string>
<string name="rtt_mode_more_information" msgid="587500128658756318">"RTT helpt bellers die doof of slechthorend zijn, een spraakbeperking hebben of meer dan alleen een stem nodig hebben.<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>Meer informatie</a>\n <br><br> - RTT-gesprekken worden opgeslagen als berichttranscript.\n <br> - RTT is niet beschikbaar voor videogesprekken"</string>
@@ -673,8 +673,8 @@
<string name="voicemail_change_pin_dialog_title" msgid="4633077715231764435">"Pincode wijzigen"</string>
<string name="preference_category_ringtone" msgid="8787281191375434976">"Ringtone en trillen"</string>
<string name="pstn_connection_service_label" msgid="9200102709997537069">"Ingebouwde simkaarten"</string>
- <string name="enable_video_calling_title" msgid="7246600931634161830">"Videogesprekken inschakelen"</string>
- <string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"Als je videogesprekken wilt inschakelen, moet je de geoptimaliseerde 4G LTE-modus inschakelen in de netwerkinstellingen."</string>
+ <string name="enable_video_calling_title" msgid="7246600931634161830">"Videogesprekken aanzetten"</string>
+ <string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"Als je videogesprekken wilt aanzetten, moet je de geoptimaliseerde 4G LTE-modus aanzetten in de netwerkinstellingen."</string>
<string name="enable_video_calling_dialog_settings" msgid="8697890611305307110">"Netwerkinstellingen"</string>
<string name="enable_video_calling_dialog_close" msgid="4298929725917045270">"Sluiten"</string>
<string name="sim_label_emergency_calls" msgid="9078241989421522310">"Noodoproepen"</string>
@@ -833,14 +833,14 @@
<string name="supp_service_over_ut_precautions_dual_sim" msgid="5166866975550910474">"Als je <xliff:g id="SUPP_SERVICE">%1$s</xliff:g> wilt gebruiken, zorg je dat je mobiele data hebt ingeschakeld voor sim <xliff:g id="SIM_NUMBER">%2$d</xliff:g>. Je kunt dit wijzigen via de mobiele netwerkinstellingen."</string>
<string name="supp_service_over_ut_precautions_roaming_dual_sim" msgid="6627654855191817965">"Als je <xliff:g id="SUPP_SERVICE">%1$s</xliff:g> wilt gebruiken, zorg je dat je mobiele data en dataroaming hebt ingeschakeld voor sim <xliff:g id="SIM_NUMBER">%2$d</xliff:g>. Je kunt dit wijzigen via de mobiele netwerkinstellingen."</string>
<string name="supp_service_over_ut_precautions_dialog_dismiss" msgid="5934541487903081652">"Sluiten"</string>
- <string name="radio_info_data_connection_enable" msgid="6183729739783252840">"Dataverbinding inschakelen"</string>
+ <string name="radio_info_data_connection_enable" msgid="6183729739783252840">"Dataverbinding aanzetten"</string>
<string name="radio_info_data_connection_disable" msgid="6404751291511368706">"Dataverbinding uitschakelen"</string>
<string name="volte_provisioned_switch_string" msgid="4812874990480336178">"VoLTE-registratie"</string>
<string name="vt_provisioned_switch_string" msgid="8295542122512195979">"Videogesprekken geregistreerd"</string>
<string name="wfc_provisioned_switch_string" msgid="3835004640321078988">"Wifi-gesprekken geregistreerd"</string>
<string name="eab_provisioned_switch_string" msgid="4449676720736033035">"EAB/aanwezigheid geregistreerd"</string>
<string name="cbrs_data_switch_string" msgid="6060356430838077653">"CBRS-gegevens"</string>
- <string name="dsds_switch_string" msgid="7564769822086764796">"DSDS inschakelen"</string>
+ <string name="dsds_switch_string" msgid="7564769822086764796">"DSDS aanzetten"</string>
<string name="dsds_dialog_title" msgid="8494569893941847575">"Apparaat opnieuw opstarten?"</string>
<string name="dsds_dialog_message" msgid="4047480385678538850">"Start het apparaat opnieuw op om de instelling te wijzigen."</string>
<string name="dsds_dialog_confirm" msgid="9032004888134129885">"Opnieuw opstarten"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 65376c9..f0da34a 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -451,8 +451,8 @@
<string name="sum_fdn" msgid="6152246141642323582">"Hantera fasta nummer"</string>
<string name="sum_fdn_change_pin" msgid="3510994280557335727">"Ändra PIN-kod för FDN-åtkomst"</string>
<string name="sum_fdn_manage_list" msgid="3311397063233992907">"Hantera nummerlistan"</string>
- <string name="voice_privacy" msgid="7346935172372181951">"Sekretess för Voice"</string>
- <string name="voice_privacy_summary" msgid="3556460926168473346">"Aktivera avancerat sekretessläge"</string>
+ <string name="voice_privacy" msgid="7346935172372181951">"Integritet för Voice"</string>
+ <string name="voice_privacy_summary" msgid="3556460926168473346">"Aktivera avancerat integritetsläge"</string>
<string name="tty_mode_option_title" msgid="3843817710032641703">"TTY-läge"</string>
<string name="tty_mode_option_summary" msgid="4770510287236494371">"Ange TTY-läge"</string>
<string name="auto_retry_mode_title" msgid="2985801935424422340">"Försök igen automatiskt"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 7125e6a..6353cb4 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -664,7 +664,7 @@
<string name="contactPhoto" msgid="7885089213135154834">"కాంటాక్ట్ ఫోటో"</string>
<string name="goPrivate" msgid="4645108311382209551">"ప్రైవేట్గా వెళ్లు"</string>
<string name="selectContact" msgid="1527612842599767382">"కాంటాక్ట్ను ఎంచుకోండి"</string>
- <string name="not_voice_capable" msgid="2819996734252084253">"వాయిస్ కాలింగ్కు మద్దతు లేదు"</string>
+ <string name="not_voice_capable" msgid="2819996734252084253">"వాయిస్ కాలింగ్కు సపోర్ట్ లేదు"</string>
<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>
@@ -890,9 +890,9 @@
<string name="radio_info_ppp_resets_label" msgid="9131901102339077661">"బూట్ చేసినప్పటి నుండి PPP రీసెట్ సంఖ్య:"</string>
<string name="radio_info_current_network_label" msgid="3052098695239642450">"ప్రస్తుత నెట్వర్క్:"</string>
<string name="radio_info_ppp_received_label" msgid="5753592451640644889">"స్వీకరించిన డేటా:"</string>
- <string name="radio_info_gsm_service_label" msgid="6443348321714241328">"వాయిస్ సేవ:"</string>
+ <string name="radio_info_gsm_service_label" msgid="6443348321714241328">"వాయిస్ సర్వీస్:"</string>
<string name="radio_info_signal_strength_label" msgid="5545444702102543260">"సిగ్నల్ సామర్థ్యం:"</string>
- <string name="radio_info_call_status_label" msgid="7693575431923095487">"వాయిస్ కాల్ స్థితి:"</string>
+ <string name="radio_info_call_status_label" msgid="7693575431923095487">"వాయిస్ కాల్ స్టేటస్:"</string>
<string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"పంపిన డేటా:"</string>
<string name="radio_info_message_waiting_label" msgid="1886549432566952078">"సందేశ నిరీక్షణ:"</string>
<string name="radio_info_phone_number_label" msgid="2533852539562512203">"ఫోన్ నంబర్:"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 04c984e..f1770e1 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -579,7 +579,7 @@
<string name="onscreenEndCallText" msgid="6138725377654842757">"สิ้นสุด"</string>
<string name="onscreenShowDialpadText" msgid="658465753816164079">"แป้นหมายเลข"</string>
<string name="onscreenMuteText" msgid="5470306116733843621">"ปิดเสียง"</string>
- <string name="onscreenAddCallText" msgid="9075675082903611677">"เพิ่มการโทร"</string>
+ <string name="onscreenAddCallText" msgid="9075675082903611677">"เพิ่มสาย"</string>
<string name="onscreenMergeCallsText" msgid="3692389519611225407">"รวมสาย"</string>
<string name="onscreenSwapCallsText" msgid="2682542150803377991">"สลับ"</string>
<string name="onscreenManageCallsText" msgid="1162047856081836469">"จัดการการโทร"</string>
diff --git a/sip/res/values-as/strings.xml b/sip/res/values-as/strings.xml
index 76a0b5f..13043e3 100644
--- a/sip/res/values-as/strings.xml
+++ b/sip/res/values-as/strings.xml
@@ -20,7 +20,7 @@
<string name="sip_accounts" msgid="7297896885665783239">"SIP একাউণ্টসমূহ"</string>
<string name="sip_accounts_title" msgid="3061686404598143943">"একাউণ্টসমূহ"</string>
<string name="sip_receive_calls" msgid="3403644006618369349">"অন্তৰ্গামী কল লাভ কৰি থকা হৈছে"</string>
- <string name="sip_receive_calls_summary" msgid="5306603671778761443">"বেটাৰি অধিক ব্যৱহাৰ কৰে"</string>
+ <string name="sip_receive_calls_summary" msgid="5306603671778761443">"বেটাৰী অধিক ব্যৱহাৰ কৰে"</string>
<string name="sip_call_options_title" msgid="5027066677561068192">"SIP কলিং ব্যৱহাৰ কৰক"</string>
<string name="sip_call_options_wifi_only_title" msgid="6663105297927456484">"SIP কলিং ব্যৱহাৰ কৰক (কেৱল ৱাই-ফাই)"</string>
<string name="sip_call_options_entry_1" msgid="4722647332760934261">"ডেটা নেটৱৰ্ক উপলব্ধ থাকোঁতে সকলো কলৰ বাবে"</string>
diff --git a/sip/res/values-kk/strings.xml b/sip/res/values-kk/strings.xml
index 1e59711..2f90a2a 100644
--- a/sip/res/values-kk/strings.xml
+++ b/sip/res/values-kk/strings.xml
@@ -17,8 +17,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="sip_settings" msgid="7452587325305604702">"SIP параметрлері"</string>
- <string name="sip_accounts" msgid="7297896885665783239">"SIP есептік жазбалары"</string>
- <string name="sip_accounts_title" msgid="3061686404598143943">"Есептік жазбалар"</string>
+ <string name="sip_accounts" msgid="7297896885665783239">"SIP аккаунттары"</string>
+ <string name="sip_accounts_title" msgid="3061686404598143943">"Аккаунттар"</string>
<string name="sip_receive_calls" msgid="3403644006618369349">"Кіріс қоңырауларды қабылдау"</string>
<string name="sip_receive_calls_summary" msgid="5306603671778761443">"Көбірек батарея қуатын пайдаланады"</string>
<string name="sip_call_options_title" msgid="5027066677561068192">"SIP қызметін пайдалану"</string>
@@ -26,11 +26,11 @@
<string name="sip_call_options_entry_1" msgid="4722647332760934261">"Деректер желісі қол жетімді болғанда барлық қоңыраулар үшін"</string>
<string name="sip_call_options_entry_2" msgid="7338504256051655013">"Тек SIP қоңыраулары үшін"</string>
<string name="sip_call_options_wifi_only_entry_1" msgid="922329055414010991">"Барлық қоңыраулар үшін"</string>
- <string name="add_sip_account" msgid="5754758646745144384">"Есептік жазба қосу"</string>
- <string name="remove_sip_account" msgid="8272617403399636513">"Есептік жазбаны жою"</string>
- <string name="sip_account_list" msgid="2596262496233721769">"SIP есептік жазбалары"</string>
- <string name="saving_account" msgid="3390358043846687266">"Есептік жазба сақталуда…"</string>
- <string name="removing_account" msgid="1544132880414780408">"Есептік жазба жойылуда…"</string>
+ <string name="add_sip_account" msgid="5754758646745144384">"Аккаунт қосу"</string>
+ <string name="remove_sip_account" msgid="8272617403399636513">"Аккаунтты жою"</string>
+ <string name="sip_account_list" msgid="2596262496233721769">"SIP аккаунттары"</string>
+ <string name="saving_account" msgid="3390358043846687266">"Аккаунт сақталуда…"</string>
+ <string name="removing_account" msgid="1544132880414780408">"Аккаунт жойылуда…"</string>
<string name="sip_menu_save" msgid="4377112554203123060">"Сақтау"</string>
<string name="sip_menu_discard" msgid="1883166691772895243">"Алып тастау"</string>
<string name="alert_dialog_close" msgid="1734746505531110706">"Профильді жабу"</string>
@@ -40,16 +40,16 @@
<string name="registration_status_registering" msgid="7986331597809521791">"Тіркелуде…"</string>
<string name="registration_status_still_trying" msgid="7178623685868766282">"Әлі әрекеттенуде…"</string>
<string name="registration_status_not_receiving" msgid="3873074208531938401">"Қоңыраулар қабылданып жатқан жоқ."</string>
- <string name="registration_status_no_data" msgid="2987064560116584121">"Есептік жазбаны тіркеу тоқтатылды, себебі интернет байланысы жоқ."</string>
- <string name="registration_status_no_wifi_data" msgid="685470618241482948">"Есептік жазбаны тіркеу тоқтатылды, себебі Wi-Fi байланысы жоқ."</string>
- <string name="registration_status_not_running" msgid="6236403137652262659">"Есептік жазба тіркеу сәтсіз аяқталды."</string>
+ <string name="registration_status_no_data" msgid="2987064560116584121">"Аккаунтты тіркеу тоқтатылды, себебі интернет байланысы жоқ."</string>
+ <string name="registration_status_no_wifi_data" msgid="685470618241482948">"Аккаунтты тіркеу тоқтатылды, себебі Wi-Fi байланысы жоқ."</string>
+ <string name="registration_status_not_running" msgid="6236403137652262659">"Аккаунт тіркеу сәтсіз аяқталды."</string>
<string name="registration_status_done" msgid="6787397199273357721">"Қоңыраулар қабылдануда."</string>
- <string name="registration_status_failed_try_later" msgid="7855389184910312091">"Есептік жазбаны тіркеу сәтсіз аяқталды: (<xliff:g id="REGISTRATION_ERROR_MESSAGE">%s</xliff:g>); әрекет кейінірек қайталанады"</string>
- <string name="registration_status_invalid_credentials" msgid="8896714049938660777">"Есептік жазбаны тіркеу сәтсіз аяқталды: Пайдаланушы аты немесе құпия сөз қате."</string>
- <string name="registration_status_server_unreachable" msgid="3832339558868965604">"Есептік жазба тіркеу сәтсіз аяқталды: Сервер атауын тексеріңіз."</string>
- <string name="third_party_account_summary" msgid="5918779106950859167">"Бұл есептік жазбаны қазіргі уақытта <xliff:g id="ACCOUNT_OWNER">%s</xliff:g> қолданбасы пайдалануда."</string>
- <string name="sip_edit_title" msgid="7438891546610820307">"SIP есептік жазбасы туралы мәліметтер"</string>
- <string name="sip_edit_new_title" msgid="8394790068979636381">"SIP есептік жазбасы туралы мәліметтер"</string>
+ <string name="registration_status_failed_try_later" msgid="7855389184910312091">"Аккаунтты тіркеу сәтсіз аяқталды: (<xliff:g id="REGISTRATION_ERROR_MESSAGE">%s</xliff:g>); әрекет кейінірек қайталанады"</string>
+ <string name="registration_status_invalid_credentials" msgid="8896714049938660777">"Аккаунтты тіркеу сәтсіз аяқталды: Пайдаланушы аты немесе құпия сөз қате."</string>
+ <string name="registration_status_server_unreachable" msgid="3832339558868965604">"Аккаунт тіркеу сәтсіз аяқталды: Сервер атауын тексеріңіз."</string>
+ <string name="third_party_account_summary" msgid="5918779106950859167">"Бұл аккаунтты қазіргі уақытта <xliff:g id="ACCOUNT_OWNER">%s</xliff:g> қолданбасы пайдалануда."</string>
+ <string name="sip_edit_title" msgid="7438891546610820307">"SIP аккаунты туралы мәліметтер"</string>
+ <string name="sip_edit_new_title" msgid="8394790068979636381">"SIP аккаунты туралы мәліметтер"</string>
<string name="domain_address_title" msgid="8238078615181248579">"Сервер"</string>
<string name="username_title" msgid="298416796886107970">"Пайдаланушы аты"</string>
<string name="password_title" msgid="8035579335591959021">"Құпия сөз"</string>
@@ -68,7 +68,7 @@
<string name="optional_summary" msgid="620379377865437488">"<Міндетті емес>"</string>
<string name="advanced_settings_show" msgid="2318728080037568529">"▷ Барлығын көрсету үшін түрту"</string>
<string name="advanced_settings_hide" msgid="6200816937370652083">"▽ Барлығын жасыру үшін түрту"</string>
- <string name="all_empty_alert" msgid="6085603517610199098">"Жаңа SIP есептік жазбасының мәліметтерін енгізіңіз."</string>
+ <string name="all_empty_alert" msgid="6085603517610199098">"Жаңа SIP аккаунтының мәліметтерін енгізіңіз."</string>
<string name="empty_alert" msgid="3693655518612836718">"<xliff:g id="INPUT_FIELD_NAME">%s</xliff:g> міндетті және оны бос қалдыруға болмайды."</string>
<string name="not_a_valid_port" msgid="3664668836663491376">"Порт нөмірі 1000 және 65534 аралығында болуы керек."</string>
<string name="no_internet_available" msgid="161720645084325479">"SIP қоңырауын шалу үшін алдымен интернет қосылымын тексеріңіз."</string>
diff --git a/sip/src/com/android/services/telephony/sip/SipEditor.java b/sip/src/com/android/services/telephony/sip/SipEditor.java
index dd475e6..9efe5bf 100644
--- a/sip/src/com/android/services/telephony/sip/SipEditor.java
+++ b/sip/src/com/android/services/telephony/sip/SipEditor.java
@@ -16,7 +16,6 @@
package com.android.services.telephony.sip;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Intent;
@@ -37,6 +36,8 @@
import android.widget.Button;
import android.widget.Toast;
+import com.android.phone.FrameworksUtils;
+
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
@@ -93,7 +94,7 @@
public Dialog onCreateDialog(Bundle savedInstanceState) {
String message = getArguments().getString(KEY_MESSAGE);
- return new AlertDialog.Builder(getActivity())
+ return FrameworksUtils.makeAlertDialogBuilder(getActivity())
.setTitle(android.R.string.dialog_alert_title)
.setIconAttribute(android.R.attr.alertDialogIcon)
.setMessage(message)
diff --git a/sip/src/com/android/services/telephony/sip/SipSettings.java b/sip/src/com/android/services/telephony/sip/SipSettings.java
index 813ba51..5137963 100644
--- a/sip/src/com/android/services/telephony/sip/SipSettings.java
+++ b/sip/src/com/android/services/telephony/sip/SipSettings.java
@@ -17,7 +17,6 @@
package com.android.services.telephony.sip;
import android.app.ActionBar;
-import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -39,6 +38,7 @@
import android.view.Menu;
import android.view.MenuItem;
+import com.android.phone.FrameworksUtils;
import com.android.phone.R;
import java.io.IOException;
@@ -287,7 +287,7 @@
startSipEditor(profile);
return;
}
- new AlertDialog.Builder(this)
+ FrameworksUtils.makeAlertDialogBuilder(this)
.setTitle(R.string.alert_dialog_close)
.setIconAttribute(android.R.attr.alertDialogIcon)
.setPositiveButton(R.string.close_profile,
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index ec6ea2b..d249fae 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -220,7 +220,7 @@
if (mImsMgr.isEnhanced4gLteModeSettingEnabledByUser()) {
mImsMgr.setVtSetting((boolean) objValue);
} else {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ AlertDialog.Builder builder = FrameworksUtils.makeAlertDialogBuilder(this);
DialogInterface.OnClickListener networkSettingsClickListener =
new Dialog.OnClickListener() {
@Override
diff --git a/src/com/android/phone/CallForwardEditPreference.java b/src/com/android/phone/CallForwardEditPreference.java
index bf296f9..db1c5b4 100644
--- a/src/com/android/phone/CallForwardEditPreference.java
+++ b/src/com/android/phone/CallForwardEditPreference.java
@@ -427,7 +427,8 @@
default: // not reachable
s = getContext().getText(R.string.disable_cfnrc_forbidden);
}
- AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ AlertDialog.Builder builder =
+ FrameworksUtils.makeAlertDialogBuilder(getContext());
builder.setNeutralButton(R.string.close_dialog, null);
builder.setTitle(getContext()
.getText(R.string.error_updating_title));
@@ -440,7 +441,8 @@
// Handle the fail-to-enable case.
CharSequence s = getContext()
.getText(R.string.registration_cf_forbidden);
- AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ AlertDialog.Builder builder =
+ FrameworksUtils.makeAlertDialogBuilder(getContext());
builder.setNeutralButton(R.string.close_dialog, null);
builder.setTitle(getContext()
.getText(R.string.error_updating_title));
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index a8ef220..7f61f78 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -276,23 +276,10 @@
private int mState;
// The possible tones we can play.
public static final int TONE_NONE = 0;
- public static final int TONE_CALL_WAITING = 1;
- public static final int TONE_BUSY = 2;
- public static final int TONE_CONGESTION = 3;
- public static final int TONE_CALL_ENDED = 4;
public static final int TONE_VOICE_PRIVACY = 5;
- public static final int TONE_REORDER = 6;
- public static final int TONE_INTERCEPT = 7;
- public static final int TONE_CDMA_DROP = 8;
- public static final int TONE_OUT_OF_SERVICE = 9;
- public static final int TONE_REDIAL = 10;
- public static final int TONE_OTA_CALL_END = 11;
- public static final int TONE_UNOBTAINABLE_NUMBER = 13;
// The tone volume relative to other sounds in the stream
- static final int TONE_RELATIVE_VOLUME_EMERGENCY = 100;
static final int TONE_RELATIVE_VOLUME_HIPRI = 80;
- static final int TONE_RELATIVE_VOLUME_LOPRI = 50;
// Buffer time (in msec) to add on to tone timeout value.
// Needed mainly when the timeout value for a tone is the
@@ -320,70 +307,11 @@
int phoneType = mCM.getFgPhone().getPhoneType();
switch (mToneId) {
- case TONE_CALL_WAITING:
- toneType = ToneGenerator.TONE_SUP_CALL_WAITING;
- toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
- // Call waiting tone is stopped by stopTone() method
- toneLengthMillis = Integer.MAX_VALUE - TONE_TIMEOUT_BUFFER;
- break;
- case TONE_BUSY:
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- toneType = ToneGenerator.TONE_CDMA_NETWORK_BUSY_ONE_SHOT;
- toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
- toneLengthMillis = 1000;
- } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM
- || phoneType == PhoneConstants.PHONE_TYPE_SIP
- || phoneType == PhoneConstants.PHONE_TYPE_IMS
- || phoneType == PhoneConstants.PHONE_TYPE_THIRD_PARTY) {
- toneType = ToneGenerator.TONE_SUP_BUSY;
- toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
- toneLengthMillis = 4000;
- } else {
- throw new IllegalStateException("Unexpected phone type: " + phoneType);
- }
- break;
- case TONE_CONGESTION:
- toneType = ToneGenerator.TONE_SUP_CONGESTION;
- toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
- toneLengthMillis = 4000;
- break;
-
- case TONE_CALL_ENDED:
- toneType = ToneGenerator.TONE_PROP_PROMPT;
- toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
- toneLengthMillis = 200;
- break;
case TONE_VOICE_PRIVACY:
toneType = ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE;
toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
toneLengthMillis = 5000;
break;
- case TONE_REORDER:
- toneType = ToneGenerator.TONE_CDMA_REORDER;
- toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
- toneLengthMillis = 4000;
- break;
- case TONE_INTERCEPT:
- toneType = ToneGenerator.TONE_CDMA_ABBR_INTERCEPT;
- toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
- toneLengthMillis = 500;
- break;
- case TONE_CDMA_DROP:
- case TONE_OUT_OF_SERVICE:
- toneType = ToneGenerator.TONE_CDMA_CALLDROP_LITE;
- toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
- toneLengthMillis = 375;
- break;
- case TONE_REDIAL:
- toneType = ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE;
- toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
- toneLengthMillis = 5000;
- break;
- case TONE_UNOBTAINABLE_NUMBER:
- toneType = ToneGenerator.TONE_SUP_ERROR;
- toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
- toneLengthMillis = 4000;
- break;
default:
throw new IllegalArgumentException("Bad toneId: " + mToneId);
}
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index a463243..45ca974 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -65,6 +65,7 @@
import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.telephony.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.telephony.Rlog;
import java.io.File;
import java.io.FileDescriptor;
@@ -1064,6 +1065,7 @@
}
String fileName;
+ String iccid = null;
if (isNoSimConfig) {
fileName = getFilenameForNoSimConfig(packageName);
} else {
@@ -1073,7 +1075,7 @@
return null;
}
- final String iccid = getIccIdForPhoneId(phoneId);
+ iccid = getIccIdForPhoneId(phoneId);
final int cid = getSpecificCarrierIdForPhoneId(phoneId);
if (iccid == null) {
loge("Cannot restore config with null iccid.");
@@ -1102,7 +1104,15 @@
} catch (FileNotFoundException e) {
// Missing file is normal occurrence that might occur with a new sim or when restoring
// an override file during boot and should not be treated as an error.
- if (file != null) logd("File not found: " + file.getPath());
+ if (file != null) {
+ if (isNoSimConfig) {
+ logd("File not found: " + file.getPath());
+ } else {
+ String filePath = file.getPath();
+ filePath = getFilePathForLogging(filePath, iccid);
+ logd("File not found : " + filePath);
+ }
+ }
} catch (IOException e) {
loge(e.toString());
}
@@ -1110,6 +1120,22 @@
return restoredBundle;
}
+ /**
+ * This method will mask most part of iccid in the filepath for logging on userbuild
+ */
+ private String getFilePathForLogging(String filePath, String iccid) {
+ // If loggable then return with actual file path
+ if (Rlog.isLoggable(LOG_TAG, Log.VERBOSE)) {
+ return filePath;
+ }
+ String path = filePath;
+ int length = (iccid != null) ? iccid.length() : 0;
+ if (length > 5 && filePath != null) {
+ path = filePath.replace(iccid.substring(5), "***************");
+ }
+ return path;
+ }
+
private PersistableBundle restoreConfigFromXml(String packageName, @NonNull String extraString,
int phoneId) {
return restoreConfigFromXml(packageName, extraString, phoneId, false);
diff --git a/src/com/android/phone/CdmaCallWaitingPreference.java b/src/com/android/phone/CdmaCallWaitingPreference.java
index 3713b19..6ff47d2 100644
--- a/src/com/android/phone/CdmaCallWaitingPreference.java
+++ b/src/com/android/phone/CdmaCallWaitingPreference.java
@@ -78,7 +78,7 @@
public void onClick() {
super.onClick();
- AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+ AlertDialog.Builder builder = FrameworksUtils.makeAlertDialogBuilder(mContext);
builder.setTitle(mContext.getText(R.string.cdma_call_waiting));
builder.setMessage(mContext.getText(R.string.enable_cdma_call_waiting_setting));
builder.setPositiveButton(R.string.enable_cdma_cw, new DialogInterface.OnClickListener() {
diff --git a/src/com/android/phone/ChangeIccPinScreen.java b/src/com/android/phone/ChangeIccPinScreen.java
index 70bf431..5369aa3 100644
--- a/src/com/android/phone/ChangeIccPinScreen.java
+++ b/src/com/android/phone/ChangeIccPinScreen.java
@@ -277,7 +277,7 @@
private AlertDialog mPUKAlert;
private void displayPUKAlert () {
if (mPUKAlert == null) {
- mPUKAlert = new AlertDialog.Builder(this)
+ mPUKAlert = FrameworksUtils.makeAlertDialogBuilder(this)
.setMessage (R.string.puk_requested)
.setCancelable(false)
.show();
diff --git a/src/com/android/phone/FrameworksUtils.java b/src/com/android/phone/FrameworksUtils.java
new file mode 100644
index 0000000..dcf10bd
--- /dev/null
+++ b/src/com/android/phone/FrameworksUtils.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 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;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.res.Configuration;
+
+/**
+ * This class provides utility functions over framework APIs
+ */
+public class FrameworksUtils {
+ /**
+ * Create a new instance of {@link AlertDialog.Builder}.
+ * @param context reference to a Context
+ * @return an instance of AlertDialog.Builder
+ */
+ public static AlertDialog.Builder makeAlertDialogBuilder(Context context) {
+ boolean isDarkTheme = (context.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+ return new AlertDialog.Builder(context, isDarkTheme
+ ? android.R.style.Theme_DeviceDefault_Dialog_Alert : 0);
+ }
+}
diff --git a/src/com/android/phone/ImsStateCallbackController.java b/src/com/android/phone/ImsStateCallbackController.java
new file mode 100644
index 0000000..c7f15bf
--- /dev/null
+++ b/src/com/android/phone/ImsStateCallbackController.java
@@ -0,0 +1,1088 @@
+/*
+ * Copyright (C) 2021 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;
+
+import static android.telephony.ims.ImsStateCallback.REASON_IMS_SERVICE_DISCONNECTED;
+import static android.telephony.ims.ImsStateCallback.REASON_IMS_SERVICE_NOT_READY;
+import static android.telephony.ims.ImsStateCallback.REASON_NO_IMS_SERVICE_CONFIGURED;
+import static android.telephony.ims.ImsStateCallback.REASON_SUBSCRIPTION_INACTIVE;
+import static android.telephony.ims.ImsStateCallback.REASON_UNKNOWN_PERMANENT_ERROR;
+import static android.telephony.ims.ImsStateCallback.REASON_UNKNOWN_TEMPORARY_ERROR;
+import static android.telephony.ims.feature.ImsFeature.FEATURE_MMTEL;
+import static android.telephony.ims.feature.ImsFeature.FEATURE_RCS;
+import static android.telephony.ims.feature.ImsFeature.STATE_READY;
+import static android.telephony.ims.feature.ImsFeature.STATE_UNAVAILABLE;
+
+import static com.android.ims.FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED;
+import static com.android.ims.FeatureConnector.UNAVAILABLE_REASON_IMS_UNSUPPORTED;
+import static com.android.ims.FeatureConnector.UNAVAILABLE_REASON_NOT_READY;
+import static com.android.ims.FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyRegistryManager;
+import android.telephony.ims.feature.ImsFeature;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.ims.FeatureConnector;
+import com.android.ims.ImsManager;
+import com.android.ims.RcsFeatureManager;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.IImsStateCallback;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConfigurationManager;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.ims.ImsResolver;
+import com.android.internal.telephony.util.HandlerExecutor;
+import com.android.services.telephony.rcs.RcsFeatureController;
+import com.android.telephony.Rlog;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.concurrent.Executor;
+
+/**
+ * Implementation of the controller managing {@link ImsStateCallback}s
+ */
+public class ImsStateCallbackController {
+ private static final String TAG = "ImsStateCallbackController";
+ private static final boolean VDBG = false;
+
+ /**
+ * Create a FeatureConnector for this class to use to connect to an ImsManager.
+ */
+ @VisibleForTesting
+ public interface MmTelFeatureConnectorFactory {
+ /**
+ * Create a FeatureConnector for this class to use to connect to an ImsManager.
+ * @param listener will receive ImsManager instance.
+ * @param executor that the Listener callbacks will be called on.
+ * @return A FeatureConnector
+ */
+ FeatureConnector<ImsManager> create(Context context, int slotId,
+ String logPrefix, FeatureConnector.Listener<ImsManager> listener,
+ Executor executor);
+ }
+
+ /**
+ * Create a FeatureConnector for this class to use to connect to an RcsFeatureManager.
+ */
+ @VisibleForTesting
+ public interface RcsFeatureConnectorFactory {
+ /**
+ * Create a FeatureConnector for this class to use to connect to an RcsFeatureManager.
+ * @param listener will receive RcsFeatureManager instance.
+ * @param executor that the Listener callbacks will be called on.
+ * @return A FeatureConnector
+ */
+ FeatureConnector<RcsFeatureManager> create(Context context, int slotId,
+ FeatureConnector.Listener<RcsFeatureManager> listener,
+ Executor executor, String logPrefix);
+ }
+
+ /** Indicates that the state is not valid, used in ExternalRcsFeatureState only */
+ private static final int STATE_UNKNOWN = -1;
+
+ /** The unavailable reason of ImsFeature is not initialized */
+ private static final int NOT_INITIALIZED = -1;
+ /** The ImsFeature is available. */
+ private static final int AVAILABLE = 0;
+
+ private static final int EVENT_SUB_CHANGED = 1;
+ private static final int EVENT_REGISTER_CALLBACK = 2;
+ private static final int EVENT_UNREGISTER_CALLBACK = 3;
+ private static final int EVENT_CARRIER_CONFIG_CHANGED = 4;
+ private static final int EVENT_EXTERNAL_RCS_STATE_CHANGED = 5;
+ private static final int EVENT_MSIM_CONFIGURATION_CHANGE = 6;
+
+ private static ImsStateCallbackController sInstance;
+
+ /**
+ * get the instance
+ */
+ public static ImsStateCallbackController getInstance() {
+ synchronized (ImsStateCallbackController.class) {
+ return sInstance;
+ }
+ }
+
+ private final PhoneGlobals mApp;
+ private final Handler mHandler;
+ private final ImsResolver mImsResolver;
+ private final SparseArray<MmTelFeatureListener> mMmTelFeatureListeners = new SparseArray<>();
+ private final SparseArray<RcsFeatureListener> mRcsFeatureListeners = new SparseArray<>();
+
+ private final SubscriptionManager mSubscriptionManager;
+ private final TelephonyRegistryManager mTelephonyRegistryManager;
+ private MmTelFeatureConnectorFactory mMmTelFeatureFactory;
+ private RcsFeatureConnectorFactory mRcsFeatureFactory;
+
+ private HashMap<IBinder, CallbackWrapper> mWrappers = new HashMap<>();
+
+ private int mNumSlots;
+
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent == null) {
+ return;
+ }
+ if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
+ Bundle bundle = intent.getExtras();
+ if (bundle == null) {
+ return;
+ }
+ int slotId = bundle.getInt(CarrierConfigManager.EXTRA_SLOT_INDEX,
+ SubscriptionManager.INVALID_PHONE_INDEX);
+ int subId = bundle.getInt(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+ if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ loge("onReceive ACTION_CARRIER_CONFIG_CHANGED invalid slotId");
+ return;
+ }
+
+ if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ loge("onReceive ACTION_CARRIER_CONFIG_CHANGED invalid subId");
+ //subscription changed will be notified by mSubChangedListener
+ return;
+ }
+
+ notifyCarrierConfigChanged(slotId);
+ }
+ }
+ };
+
+ private final SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener =
+ new SubscriptionManager.OnSubscriptionsChangedListener() {
+ @Override
+ public void onSubscriptionsChanged() {
+ if (!mHandler.hasMessages(EVENT_SUB_CHANGED)) {
+ mHandler.sendEmptyMessage(EVENT_SUB_CHANGED);
+ }
+ }
+ };
+
+ private final class MyHandler extends Handler {
+ MyHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ logv("handleMessage: " + msg);
+ switch (msg.what) {
+ case EVENT_SUB_CHANGED:
+ onSubChanged();
+ break;
+
+ case EVENT_REGISTER_CALLBACK:
+ onRegisterCallback((ImsStateCallbackController.CallbackWrapper) msg.obj);
+ break;
+
+ case EVENT_UNREGISTER_CALLBACK:
+ onUnregisterCallback((IImsStateCallback) msg.obj);
+ break;
+
+ case EVENT_CARRIER_CONFIG_CHANGED:
+ onCarrierConfigChanged(msg.arg1);
+ break;
+
+ case EVENT_EXTERNAL_RCS_STATE_CHANGED:
+ if (msg.obj == null) break;
+ onExternalRcsStateChanged((ExternalRcsFeatureState) msg.obj);
+ break;
+
+ case EVENT_MSIM_CONFIGURATION_CHANGE:
+ AsyncResult result = (AsyncResult) msg.obj;
+ Integer numSlots = (Integer) result.result;
+ if (numSlots == null) {
+ Log.w(TAG, "msim config change with null num slots");
+ break;
+ }
+ updateFeatureControllerSize(numSlots);
+ break;
+
+ default:
+ loge("Unhandled event " + msg.what);
+ }
+ }
+ }
+
+ private final class MmTelFeatureListener implements FeatureConnector.Listener<ImsManager> {
+ private FeatureConnector<ImsManager> mConnector;
+ private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private int mState = STATE_UNAVAILABLE;
+ private int mReason = REASON_IMS_SERVICE_DISCONNECTED;
+
+ /*
+ * Remember the last return of verifyImsMmTelConfigured().
+ * true means ImsResolver found an IMS package for FEATURE_MMTEL.
+ *
+ * mReason is updated through connectionUnavailable triggered by ImsResolver.
+ * mHasConfig is update through notifyConfigChanged triggered by mReceiver.
+ * mHasConfig can be a redundancy of (mReason == REASON_NO_IMS_SERVICE_CONFIGURED).
+ * However, when a carrier config changes, we are not sure the order
+ * of execution of connectionUnavailable and notifyConfigChanged.
+ * So, it's safe to use a separated state to retain it.
+ * We assume mHasConfig is true, until it's determined explicitly.
+ */
+ private boolean mHasConfig = true;
+
+ private int mSlotId = -1;
+ private String mLogPrefix = "";
+
+ MmTelFeatureListener(int slotId) {
+ mLogPrefix = "[MMTEL, " + slotId + "] ";
+ logv(mLogPrefix + "create");
+ mConnector = mMmTelFeatureFactory.create(
+ mApp, slotId, TAG, this, new HandlerExecutor(mHandler));
+ mConnector.connect();
+ }
+
+ void setSubId(int subId) {
+ logv(mLogPrefix + "setSubId mSubId=" + mSubId + ", subId=" + subId);
+ if (mSubId == subId) return;
+ logd(mLogPrefix + "setSubId subId changed");
+
+ mSubId = subId;
+ }
+
+ void destroy() {
+ logv(mLogPrefix + "destroy");
+ mConnector.disconnect();
+ mConnector = null;
+ }
+
+ @Override
+ public void connectionReady(ImsManager manager) {
+ logd(mLogPrefix + "connectionReady");
+
+ mState = STATE_READY;
+ mReason = AVAILABLE;
+ mHasConfig = true;
+ onFeatureStateChange(mSubId, FEATURE_MMTEL, mState, mReason);
+ }
+
+ @Override
+ public void connectionUnavailable(int reason) {
+ logd(mLogPrefix + "connectionUnavailable reason=" + connectorReasonToString(reason));
+
+ reason = convertReasonType(reason);
+ if (mReason == reason) return;
+
+ connectionUnavailableInternal(reason);
+ }
+
+ private void connectionUnavailableInternal(int reason) {
+ mState = STATE_UNAVAILABLE;
+ mReason = reason;
+
+ /* If having no IMS package for MMTEL,
+ * dicard the reason except REASON_NO_IMS_SERVICE_CONFIGURED. */
+ if (!mHasConfig && reason != REASON_NO_IMS_SERVICE_CONFIGURED) return;
+
+ onFeatureStateChange(mSubId, FEATURE_MMTEL, mState, mReason);
+ }
+
+ void notifyConfigChanged(boolean hasConfig) {
+ if (mHasConfig == hasConfig) return;
+
+ logd(mLogPrefix + "notifyConfigChanged " + hasConfig);
+
+ mHasConfig = hasConfig;
+ if (hasConfig) {
+ // REASON_NO_IMS_SERVICE_CONFIGURED is already reported to the clients,
+ // since there is no configuration of IMS package for MMTEL.
+ // Now, a carrier configuration change is notified and
+ // the response from ImsResolver is changed from false to true.
+ if (mState != STATE_READY) {
+ if (mReason == REASON_NO_IMS_SERVICE_CONFIGURED) {
+ // In this case, notify clients the reason, REASON_DISCONNCTED,
+ // to update the state.
+ connectionUnavailable(UNAVAILABLE_REASON_DISCONNECTED);
+ } else {
+ // ImsResolver and ImsStateCallbackController run with different Looper.
+ // In this case, FeatureConnectorListener is updated ahead of this.
+ // But, connectionUnavailable didn't notify clients since mHasConfig is
+ // false. So, notify clients here.
+ connectionUnavailableInternal(mReason);
+ }
+ }
+ } else {
+ // FeatureConnector doesn't report UNAVAILABLE_REASON_IMS_UNSUPPORTED,
+ // so report the reason here.
+ connectionUnavailable(UNAVAILABLE_REASON_IMS_UNSUPPORTED);
+ }
+ }
+
+ // called from onRegisterCallback
+ boolean notifyState(CallbackWrapper wrapper) {
+ logv(mLogPrefix + "notifyState subId=" + wrapper.mSubId);
+
+ return wrapper.notifyState(mSubId, FEATURE_MMTEL, mState, mReason);
+ }
+ }
+
+ private final class RcsFeatureListener implements FeatureConnector.Listener<RcsFeatureManager> {
+ private FeatureConnector<RcsFeatureManager> mConnector;
+ private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private int mState = STATE_UNAVAILABLE;
+ private int mReason = REASON_IMS_SERVICE_DISCONNECTED;
+
+ /*
+ * Remember the last return of verifyImsMmTelConfigured().
+ * true means ImsResolver found an IMS package for FEATURE_RCS.
+ *
+ * mReason is updated through connectionUnavailable triggered by ImsResolver.
+ * mHasConfig is update through notifyConfigChanged triggered by mReceiver,
+ * and notifyExternalRcsState which triggered by TelephonyRcsService refers it.
+ * mHasConfig can be a redundancy of (mReason == REASON_NO_IMS_SERVICE_CONFIGURED).
+ * However, when a carrier config changes, we are not sure the order
+ * of execution of connectionUnavailable, notifyConfigChanged and notifyExternalRcsState.
+ * So, it's safe to use a separated state to retain it.
+ * We assume mHasConfig is true, until it's determined explicitly.
+ */
+ private boolean mHasConfig = true;
+
+ /*
+ * TelephonyRcsService doesn’t try to connect to RcsFeature if there is no active feature
+ * for a given subscription. The active features are declared by carrier configs and
+ * configuration resources. The APIs of ImsRcsManager and SipDelegateManager are available
+ * only when the RcsFeatureController has a STATE_READY state connection.
+ * This configuration is different from the configuration of IMS package for RCS.
+ * ImsStateCallbackController's FeatureConnectorListener can be STATE_READY state,
+ * even in case there is no active RCS feature. But Manager's APIs throws exception.
+ *
+ * For RCS, in addition to mHasConfig, the sate of TelephonyRcsService and
+ * RcsFeatureConnector will be traced to determine the state to be notified to clients.
+ */
+ private ExternalRcsFeatureState mExternalState = null;
+
+ private int mSlotId = -1;
+ private String mLogPrefix = "";
+
+ RcsFeatureListener(int slotId) {
+ mLogPrefix = "[RCS, " + slotId + "] ";
+ logv(mLogPrefix + "create");
+
+ mConnector = mRcsFeatureFactory.create(
+ mApp, slotId, this, new HandlerExecutor(mHandler), TAG);
+ mConnector.connect();
+ }
+
+ void setSubId(int subId) {
+ logv(mLogPrefix + "setSubId mSubId=" + mSubId + ", subId=" + subId);
+ if (mSubId == subId) return;
+ logd(mLogPrefix + "setSubId subId changed");
+
+ mSubId = subId;
+ }
+
+ void destroy() {
+ logv(mLogPrefix + "destroy");
+
+ mConnector.disconnect();
+ mConnector = null;
+ }
+
+ @Override
+ public void connectionReady(RcsFeatureManager manager) {
+ logd(mLogPrefix + "connectionReady");
+
+ mState = STATE_READY;
+ mReason = AVAILABLE;
+ mHasConfig = true;
+
+ if (mExternalState != null && mExternalState.isReady()) {
+ onFeatureStateChange(mSubId, FEATURE_RCS, mState, mReason);
+ }
+ }
+
+ @Override
+ public void connectionUnavailable(int reason) {
+ logd(mLogPrefix + "connectionUnavailable reason=" + connectorReasonToString(reason));
+
+ reason = convertReasonType(reason);
+ if (mReason == reason) return;
+
+ connectionUnavailableInternal(reason);
+ }
+
+ private void connectionUnavailableInternal(int reason) {
+ mState = STATE_UNAVAILABLE;
+ mReason = reason;
+
+ /* If having no IMS package for RCS,
+ * dicard the reason except REASON_NO_IMS_SERVICE_CONFIGURED. */
+ if (!mHasConfig && reason != REASON_NO_IMS_SERVICE_CONFIGURED) return;
+
+ if (mExternalState == null && reason != REASON_NO_IMS_SERVICE_CONFIGURED) {
+ // Wait until TelephonyRcsService notifies its state.
+ return;
+ }
+
+ if (mExternalState != null && !mExternalState.hasActiveFeatures()) {
+ // notifyExternalState has notified REASON_NO_IMS_SERVICE_CONFIGURED already
+ // ignore it
+ return;
+ }
+
+ if ((mExternalState != null && mExternalState.hasActiveFeatures())
+ || mReason == REASON_NO_IMS_SERVICE_CONFIGURED) {
+ onFeatureStateChange(mSubId, FEATURE_RCS, mState, mReason);
+ }
+ }
+
+ void notifyConfigChanged(boolean hasConfig) {
+ if (mHasConfig == hasConfig) return;
+
+ logd(mLogPrefix + "notifyConfigChanged " + hasConfig);
+
+ mHasConfig = hasConfig;
+ if (hasConfig) {
+ // REASON_NO_IMS_SERVICE_CONFIGURED is already reported to the clients,
+ // since there is no configuration of IMS package for RCS.
+ // Now, a carrier configuration change is notified and
+ // the response from ImsResolver is changed from false to true.
+ if (mState != STATE_READY) {
+ if (mReason == REASON_NO_IMS_SERVICE_CONFIGURED) {
+ // In this case, notify clients the reason, REASON_DISCONNCTED,
+ // to update the state.
+ connectionUnavailable(UNAVAILABLE_REASON_DISCONNECTED);
+ } else {
+ // ImsResolver and ImsStateCallbackController run with different Looper.
+ // In this case, FeatureConnectorListener is updated ahead of this.
+ // But, connectionUnavailable didn't notify clients since mHasConfig is
+ // false. So, notify clients here.
+ connectionUnavailableInternal(mReason);
+ }
+ }
+ } else {
+ // FeatureConnector doesn't report UNAVAILABLE_REASON_IMS_UNSUPPORTED,
+ // so report the reason here.
+ connectionUnavailable(UNAVAILABLE_REASON_IMS_UNSUPPORTED);
+ }
+ }
+
+ void notifyExternalRcsState(ExternalRcsFeatureState fs) {
+ logv(mLogPrefix + "notifyExternalRcsState"
+ + " state=" + (fs.mState == STATE_UNKNOWN
+ ? "" : ImsFeature.STATE_LOG_MAP.get(fs.mState))
+ + ", reason=" + imsStateReasonToString(fs.mReason));
+
+ ExternalRcsFeatureState oldFs = mExternalState;
+ // External state is from TelephonyRcsService while a feature is added or removed.
+ if (fs.mState == STATE_UNKNOWN) {
+ if (oldFs != null) fs.mState = oldFs.mState;
+ else fs.mState = STATE_UNAVAILABLE;
+ }
+
+ mExternalState = fs;
+
+ // No IMS package found.
+ // REASON_NO_IMS_SERVICE_CONFIGURED is notified to clients already.
+ if (!mHasConfig) return;
+
+ if (fs.hasActiveFeatures()) {
+ if (mState == STATE_READY) {
+ if ((oldFs == null || !oldFs.isReady()) && fs.isReady()) {
+ // it is waiting RcsFeatureConnector's notification.
+ // notify clients here.
+ onFeatureStateChange(mSubId, FEATURE_RCS, mState, mReason);
+ } else if (!fs.isReady()) {
+ // Wait RcsFeatureConnector's notification
+ } else {
+ // ignore duplicated notification
+ }
+ }
+ } else {
+ // notify only once
+ if (oldFs == null || oldFs.hasActiveFeatures()) {
+ if (mReason != REASON_NO_IMS_SERVICE_CONFIGURED) {
+ onFeatureStateChange(
+ mSubId, FEATURE_RCS, STATE_UNAVAILABLE,
+ REASON_NO_IMS_SERVICE_CONFIGURED);
+ }
+ } else {
+ // ignore duplicated notification
+ }
+ }
+ }
+
+ // called from onRegisterCallback
+ boolean notifyState(CallbackWrapper wrapper) {
+ logv(mLogPrefix + "notifyState subId=" + wrapper.mSubId);
+
+ if (mHasConfig) {
+ if (mExternalState == null) {
+ // Wait until TelephonyRcsService notifies its state.
+ return wrapper.notifyState(mSubId, FEATURE_RCS, STATE_UNAVAILABLE,
+ REASON_IMS_SERVICE_DISCONNECTED);
+ } else if (!mExternalState.hasActiveFeatures()) {
+ return wrapper.notifyState(mSubId, FEATURE_RCS, STATE_UNAVAILABLE,
+ REASON_NO_IMS_SERVICE_CONFIGURED);
+ }
+ }
+
+ return wrapper.notifyState(mSubId, FEATURE_RCS, mState, mReason);
+ }
+ }
+
+ /**
+ * A wrapper class for the callback registered
+ */
+ private static class CallbackWrapper {
+ private final int mSubId;
+ private final int mRequiredFeature;
+ private final IImsStateCallback mCallback;
+ private final IBinder mBinder;
+
+ CallbackWrapper(int subId, int feature, IImsStateCallback callback) {
+ mSubId = subId;
+ mRequiredFeature = feature;
+ mCallback = callback;
+ mBinder = callback.asBinder();
+ }
+
+ /**
+ * @return false when accessing callback binder throws an Exception.
+ * That means the callback binder is not valid any longer.
+ * The death of remote process can cause this.
+ * This instance shall be removed from the list.
+ */
+ boolean notifyState(int subId, int feature, int state, int reason) {
+ logv("CallbackWrapper notifyState subId=" + subId
+ + ", feature=" + ImsFeature.FEATURE_LOG_MAP.get(feature)
+ + ", state=" + ImsFeature.STATE_LOG_MAP.get(state)
+ + ", reason=" + imsStateReasonToString(reason));
+
+ try {
+ if (state == STATE_READY) {
+ mCallback.onAvailable();
+ } else {
+ mCallback.onUnavailable(reason);
+ }
+ } catch (Exception e) {
+ loge("CallbackWrapper notifyState e=" + e);
+ return false;
+ }
+
+ return true;
+ }
+
+ void notifyInactive() {
+ logv("CallbackWrapper notifyInactive subId=" + mSubId);
+
+ try {
+ mCallback.onUnavailable(REASON_SUBSCRIPTION_INACTIVE);
+ } catch (Exception e) {
+ // ignored
+ }
+ }
+ }
+
+ private static class ExternalRcsFeatureState {
+ private int mSlotId;
+ private int mState = STATE_UNAVAILABLE;
+ private int mReason = NOT_INITIALIZED;
+
+ ExternalRcsFeatureState(int slotId, int state, int reason) {
+ mSlotId = slotId;
+ mState = state;
+ mReason = reason;
+ }
+
+ boolean hasActiveFeatures() {
+ return mReason != REASON_NO_IMS_SERVICE_CONFIGURED;
+ }
+
+ boolean isReady() {
+ return mState == STATE_READY;
+ }
+ }
+
+ /**
+ * create an instance
+ */
+ public static ImsStateCallbackController make(PhoneGlobals app, int numSlots) {
+ synchronized (ImsStateCallbackController.class) {
+ if (sInstance == null) {
+ logd("ImsStateCallbackController created");
+
+ HandlerThread handlerThread = new HandlerThread(TAG);
+ handlerThread.start();
+ sInstance = new ImsStateCallbackController(app, handlerThread.getLooper(), numSlots,
+ ImsManager::getConnector, RcsFeatureManager::getConnector,
+ ImsResolver.getInstance());
+ }
+ }
+ return sInstance;
+ }
+
+ @VisibleForTesting
+ public ImsStateCallbackController(PhoneGlobals app, Looper looper, int numSlots,
+ MmTelFeatureConnectorFactory mmTelFactory, RcsFeatureConnectorFactory rcsFactory,
+ ImsResolver imsResolver) {
+ mApp = app;
+ mHandler = new MyHandler(looper);
+ mImsResolver = imsResolver;
+ mSubscriptionManager = mApp.getSystemService(SubscriptionManager.class);
+ mTelephonyRegistryManager = mApp.getSystemService(TelephonyRegistryManager.class);
+ mMmTelFeatureFactory = mmTelFactory;
+ mRcsFeatureFactory = rcsFactory;
+
+ updateFeatureControllerSize(numSlots);
+
+ mTelephonyRegistryManager.addOnSubscriptionsChangedListener(
+ mSubChangedListener, mSubChangedListener.getHandlerExecutor());
+
+ PhoneConfigurationManager.registerForMultiSimConfigChange(mHandler,
+ EVENT_MSIM_CONFIGURATION_CHANGE, null);
+
+ mApp.registerReceiver(mReceiver, new IntentFilter(
+ CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+
+ onSubChanged();
+ }
+
+ /**
+ * Update the number of {@link RcsFeatureController}s that are created based on the number of
+ * active slots on the device.
+ */
+ @VisibleForTesting
+ public void updateFeatureControllerSize(int newNumSlots) {
+ if (mNumSlots != newNumSlots) {
+ Log.d(TAG, "updateFeatures: oldSlots=" + mNumSlots
+ + ", newNumSlots=" + newNumSlots);
+ if (mNumSlots < newNumSlots) {
+ for (int i = mNumSlots; i < newNumSlots; i++) {
+ MmTelFeatureListener m = new MmTelFeatureListener(i);
+ mMmTelFeatureListeners.put(i, m);
+ RcsFeatureListener r = new RcsFeatureListener(i);
+ mRcsFeatureListeners.put(i, r);
+ }
+ } else {
+ for (int i = (mNumSlots - 1); i > (newNumSlots - 1); i--) {
+ MmTelFeatureListener m = mMmTelFeatureListeners.get(i);
+ if (m != null) {
+ mMmTelFeatureListeners.remove(i);
+ m.destroy();
+ }
+ RcsFeatureListener r = mRcsFeatureListeners.get(i);
+ if (r != null) {
+ mRcsFeatureListeners.remove(i);
+ r.destroy();
+ }
+ }
+ }
+ }
+ mNumSlots = newNumSlots;
+ }
+
+ /**
+ * Dependencies for testing.
+ */
+ @VisibleForTesting
+ public void onSubChanged() {
+ logv("onSubChanged size=" + mWrappers.size());
+
+ for (int i = 0; i < mMmTelFeatureListeners.size(); i++) {
+ MmTelFeatureListener l = mMmTelFeatureListeners.valueAt(i);
+ l.setSubId(getSubId(i));
+ }
+
+ for (int i = 0; i < mRcsFeatureListeners.size(); i++) {
+ RcsFeatureListener l = mRcsFeatureListeners.valueAt(i);
+ l.setSubId(getSubId(i));
+ }
+
+ if (mWrappers.size() == 0) return;
+
+ ArrayList<IBinder> inactiveCallbacks = new ArrayList<>();
+ final int[] activeSubs = mSubscriptionManager.getActiveSubscriptionIdList();
+
+ logv("onSubChanged activeSubs=" + Arrays.toString(activeSubs));
+
+ // Remove callbacks for inactive subscriptions
+ for (IBinder binder : mWrappers.keySet()) {
+ CallbackWrapper wrapper = mWrappers.get(binder);
+ if (wrapper != null) {
+ if (!isActive(activeSubs, wrapper.mSubId)) {
+ // inactive subscription
+ inactiveCallbacks.add(binder);
+ }
+ } else {
+ // unexpected, remove it
+ inactiveCallbacks.add(binder);
+ }
+ }
+ removeInactiveCallbacks(inactiveCallbacks, "onSubChanged");
+ }
+
+ private void onFeatureStateChange(int subId, int feature, int state, int reason) {
+ logv("onFeatureStateChange subId=" + subId
+ + ", feature=" + ImsFeature.FEATURE_LOG_MAP.get(feature)
+ + ", state=" + ImsFeature.STATE_LOG_MAP.get(state)
+ + ", reason=" + imsStateReasonToString(reason));
+
+ ArrayList<IBinder> inactiveCallbacks = new ArrayList<>();
+ mWrappers.values().forEach(wrapper -> {
+ if (subId == wrapper.mSubId
+ && feature == wrapper.mRequiredFeature
+ && !wrapper.notifyState(subId, feature, state, reason)) {
+ // callback has exception, remove it
+ inactiveCallbacks.add(wrapper.mBinder);
+ }
+ });
+ removeInactiveCallbacks(inactiveCallbacks, "onFeatureStateChange");
+ }
+
+ private void onRegisterCallback(CallbackWrapper wrapper) {
+ if (wrapper == null) return;
+
+ logv("onRegisterCallback before size=" + mWrappers.size());
+ logv("onRegisterCallback subId=" + wrapper.mSubId
+ + ", feature=" + wrapper.mRequiredFeature);
+
+ // Not sure the following case can happen or not:
+ // step1) Subscription changed
+ // step2) ImsStateCallbackController not processed onSubChanged yet
+ // step3) Client registers with a strange subId
+ // The validity of the subId is checked PhoneInterfaceManager#registerImsStateCallback.
+ // So, register the wrapper here before trying to notifyState.
+ // TODO: implement the recovery for this case, notifying the current reson, in onSubChanged
+ mWrappers.put(wrapper.mBinder, wrapper);
+
+ if (wrapper.mRequiredFeature == FEATURE_MMTEL) {
+ for (int i = 0; i < mMmTelFeatureListeners.size(); i++) {
+ MmTelFeatureListener l = mMmTelFeatureListeners.valueAt(i);
+ if (l.mSubId == wrapper.mSubId
+ && !l.notifyState(wrapper)) {
+ mWrappers.remove(wrapper.mBinder);
+ break;
+ }
+ }
+ } else if (wrapper.mRequiredFeature == FEATURE_RCS) {
+ for (int i = 0; i < mRcsFeatureListeners.size(); i++) {
+ RcsFeatureListener l = mRcsFeatureListeners.valueAt(i);
+ if (l.mSubId == wrapper.mSubId
+ && !l.notifyState(wrapper)) {
+ mWrappers.remove(wrapper.mBinder);
+ break;
+ }
+ }
+ }
+
+ logv("onRegisterCallback after size=" + mWrappers.size());
+ }
+
+ private void onUnregisterCallback(IImsStateCallback cb) {
+ if (cb == null) return;
+ mWrappers.remove(cb.asBinder());
+ }
+
+ private void onCarrierConfigChanged(int slotId) {
+ if (slotId >= mNumSlots) {
+ logd("onCarrierConfigChanged invalid slotId "
+ + slotId + ", mNumSlots=" + mNumSlots);
+ return;
+ }
+
+ logd("onCarrierConfigChanged slotId=" + slotId);
+
+ boolean hasConfig = verifyImsMmTelConfigured(slotId);
+ if (slotId < mMmTelFeatureListeners.size()) {
+ MmTelFeatureListener listener = mMmTelFeatureListeners.valueAt(slotId);
+ listener.notifyConfigChanged(hasConfig);
+ }
+
+ hasConfig = verifyImsRcsConfigured(slotId);
+ if (slotId < mRcsFeatureListeners.size()) {
+ RcsFeatureListener listener = mRcsFeatureListeners.valueAt(slotId);
+ listener.notifyConfigChanged(hasConfig);
+ }
+ }
+
+ private void onExternalRcsStateChanged(ExternalRcsFeatureState fs) {
+ logv("onExternalRcsStateChanged slotId=" + fs.mSlotId
+ + ", state=" + (fs.mState == STATE_UNKNOWN
+ ? "" : ImsFeature.STATE_LOG_MAP.get(fs.mState))
+ + ", reason=" + imsStateReasonToString(fs.mReason));
+
+ RcsFeatureListener listener = mRcsFeatureListeners.get(fs.mSlotId);
+ if (listener != null) {
+ listener.notifyExternalRcsState(fs);
+ } else {
+ // unexpected state
+ loge("onExternalRcsStateChanged slotId=" + fs.mSlotId + ", no listener.");
+ }
+ }
+
+ /**
+ * Interface to be notified from TelephonyRcsSerice and RcsFeatureController
+ *
+ * @param ready true if feature's state is STATE_READY. Valid only when it is true.
+ * @param hasActiveFeatures true if the RcsFeatureController has active features.
+ */
+ public void notifyExternalRcsStateChanged(
+ int slotId, boolean ready, boolean hasActiveFeatures) {
+ int state = STATE_UNKNOWN;
+ int reason = REASON_IMS_SERVICE_DISCONNECTED;
+
+ if (ready) {
+ // From RcsFeatureController
+ state = STATE_READY;
+ reason = AVAILABLE;
+ } else if (!hasActiveFeatures) {
+ // From TelephonyRcsService
+ reason = REASON_NO_IMS_SERVICE_CONFIGURED;
+ state = STATE_UNAVAILABLE;
+ } else {
+ // From TelephonyRcsService
+ // TelephonyRcsService doesn't know the exact state of FeatureConnection.
+ // Only when there is no feature, we can assume the state.
+ }
+
+ logv("notifyExternalRcsStateChanged slotId=" + slotId
+ + ", ready=" + ready
+ + ", hasActiveFeatures=" + hasActiveFeatures);
+
+ ExternalRcsFeatureState fs = new ExternalRcsFeatureState(slotId, state, reason);
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_EXTERNAL_RCS_STATE_CHANGED, fs));
+ }
+
+ /**
+ * Notifies carrier configuration has changed.
+ */
+ @VisibleForTesting
+ public void notifyCarrierConfigChanged(int slotId) {
+ logv("notifyCarrierConfigChanged slotId=" + slotId);
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARRIER_CONFIG_CHANGED, slotId, 0));
+ }
+ /**
+ * Register IImsStateCallback
+ *
+ * @param feature for which state is changed, ImsFeature.FEATURE_*
+ */
+ public void registerImsStateCallback(int subId, int feature, IImsStateCallback cb) {
+ logv("registerImsStateCallback subId=" + subId + ", feature=" + feature);
+
+ CallbackWrapper wrapper = new CallbackWrapper(subId, feature, cb);
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_CALLBACK, wrapper));
+ }
+
+ /**
+ * Unegister previously registered callback
+ */
+ public void unregisterImsStateCallback(IImsStateCallback cb) {
+ logv("unregisterImsStateCallback");
+
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_CALLBACK, cb));
+ }
+
+ private void removeInactiveCallbacks(
+ ArrayList<IBinder> inactiveCallbacks, String message) {
+ if (inactiveCallbacks == null || inactiveCallbacks.size() == 0) return;
+
+ logv("removeInactiveCallbacks size=" + inactiveCallbacks.size() + " from " + message);
+
+ for (IBinder binder : inactiveCallbacks) {
+ CallbackWrapper wrapper = mWrappers.get(binder);
+ if (wrapper != null) {
+ // Send the reason REASON_SUBSCRIPTION_INACTIVE to the client
+ wrapper.notifyInactive();
+ mWrappers.remove(binder);
+ }
+ }
+ inactiveCallbacks.clear();
+ }
+
+ private int getSubId(int slotId) {
+ Phone phone = mPhoneFactoryProxy.getPhone(slotId);
+ if (phone != null) return phone.getSubId();
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
+ private static boolean isActive(final int[] activeSubs, int subId) {
+ for (int i : activeSubs) {
+ if (i == subId) return true;
+ }
+ return false;
+ }
+
+ private static int convertReasonType(int reason) {
+ switch(reason) {
+ case UNAVAILABLE_REASON_NOT_READY:
+ return REASON_IMS_SERVICE_NOT_READY;
+ case UNAVAILABLE_REASON_IMS_UNSUPPORTED:
+ return REASON_NO_IMS_SERVICE_CONFIGURED;
+ default:
+ break;
+ }
+
+ return REASON_IMS_SERVICE_DISCONNECTED;
+ }
+
+ private boolean verifyImsMmTelConfigured(int slotId) {
+ boolean ret = false;
+ if (mImsResolver == null) {
+ loge("verifyImsMmTelConfigured mImsResolver is null");
+ } else {
+ ret = mImsResolver.isImsServiceConfiguredForFeature(slotId, FEATURE_MMTEL);
+ }
+ logv("verifyImsMmTelConfigured slotId=" + slotId + ", ret=" + ret);
+ return ret;
+ }
+
+ private boolean verifyImsRcsConfigured(int slotId) {
+ boolean ret = false;
+ if (mImsResolver == null) {
+ loge("verifyImsRcsConfigured mImsResolver is null");
+ } else {
+ ret = mImsResolver.isImsServiceConfiguredForFeature(slotId, FEATURE_RCS);
+ }
+ logv("verifyImsRcsConfigured slotId=" + slotId + ", ret=" + ret);
+ return ret;
+ }
+
+ private static String connectorReasonToString(int reason) {
+ switch(reason) {
+ case UNAVAILABLE_REASON_DISCONNECTED:
+ return "DISCONNECTED";
+ case UNAVAILABLE_REASON_NOT_READY:
+ return "NOT_READY";
+ case UNAVAILABLE_REASON_IMS_UNSUPPORTED:
+ return "IMS_UNSUPPORTED";
+ case UNAVAILABLE_REASON_SERVER_UNAVAILABLE:
+ return "SERVER_UNAVAILABLE";
+ default:
+ break;
+ }
+ return "";
+ }
+
+ private static String imsStateReasonToString(int reason) {
+ switch(reason) {
+ case REASON_UNKNOWN_TEMPORARY_ERROR:
+ return "UNKNOWN_TEMPORARY_ERROR";
+ case REASON_UNKNOWN_PERMANENT_ERROR:
+ return "UNKNOWN_PERMANENT_ERROR";
+ case REASON_IMS_SERVICE_DISCONNECTED:
+ return "IMS_SERVICE_DISCONNECTED";
+ case REASON_NO_IMS_SERVICE_CONFIGURED:
+ return "NO_IMS_SERVICE_CONFIGURED";
+ case REASON_SUBSCRIPTION_INACTIVE:
+ return "SUBSCRIPTION_INACTIVE";
+ case REASON_IMS_SERVICE_NOT_READY:
+ return "IMS_SERVICE_NOT_READY";
+ default:
+ break;
+ }
+ return "";
+ }
+
+ /**
+ * PhoneFactory Dependencies for testing.
+ */
+ @VisibleForTesting
+ public interface PhoneFactoryProxy {
+ /**
+ * Override getPhone for testing.
+ */
+ Phone getPhone(int index);
+ }
+
+ private PhoneFactoryProxy mPhoneFactoryProxy = new PhoneFactoryProxy() {
+ @Override
+ public Phone getPhone(int index) {
+ return PhoneFactory.getPhone(index);
+ }
+ };
+
+ private void release() {
+ logv("release");
+
+ mTelephonyRegistryManager.removeOnSubscriptionsChangedListener(mSubChangedListener);
+ mApp.unregisterReceiver(mReceiver);
+
+ for (int i = 0; i < mMmTelFeatureListeners.size(); i++) {
+ mMmTelFeatureListeners.valueAt(i).destroy();
+ }
+ mMmTelFeatureListeners.clear();
+
+ for (int i = 0; i < mRcsFeatureListeners.size(); i++) {
+ mRcsFeatureListeners.valueAt(i).destroy();
+ }
+ mRcsFeatureListeners.clear();
+ }
+
+ /**
+ * destroy the instance
+ */
+ @VisibleForTesting
+ public void destroy() {
+ logv("destroy it");
+
+ release();
+ mHandler.getLooper().quit();
+ }
+
+ /**
+ * get the handler
+ */
+ @VisibleForTesting
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ /**
+ * Determine whether the callback is registered or not
+ */
+ @VisibleForTesting
+ public boolean isRegistered(IImsStateCallback cb) {
+ if (cb == null) return false;
+ return mWrappers.containsKey(cb.asBinder());
+ }
+
+ private static void logv(String msg) {
+ if (VDBG) {
+ Rlog.d(TAG, msg);
+ }
+ }
+
+ private static void logd(String msg) {
+ Rlog.d(TAG, msg);
+ }
+
+ private static void loge(String msg) {
+ Rlog.e(TAG, msg);
+ }
+}
diff --git a/src/com/android/phone/PhoneDisplayMessage.java b/src/com/android/phone/PhoneDisplayMessage.java
index 199fbb8..be7fc7f 100644
--- a/src/com/android/phone/PhoneDisplayMessage.java
+++ b/src/com/android/phone/PhoneDisplayMessage.java
@@ -68,7 +68,7 @@
// displaying system alert dialog on the screen instead of
// using another activity to display the message. This
// places the message at the forefront of the UI.
- sDisplayMessageDialog = new AlertDialog.Builder(context)
+ sDisplayMessageDialog = FrameworksUtils.makeAlertDialogBuilder(context)
.setIcon(android.R.drawable.ic_dialog_info)
.setTitle(title)
.setMessage(msg)
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 1a1c321..9607919 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -74,6 +74,7 @@
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
import com.android.internal.telephony.uicc.UiccCard;
+import com.android.internal.telephony.uicc.UiccPort;
import com.android.internal.telephony.uicc.UiccProfile;
import com.android.internal.util.IndentingPrintWriter;
import com.android.phone.settings.SettingsConstants;
@@ -159,6 +160,7 @@
TelephonyRcsService mTelephonyRcsService;
public PhoneInterfaceManager phoneMgr;
public ImsRcsController imsRcsController;
+ public ImsStateCallbackController mImsStateCallbackController;
CarrierConfigLoader configLoader;
private Phone phoneInEcm;
@@ -241,13 +243,13 @@
// if passed in subType is unknown, retrieve it here.
if (subType == -1) {
- final UiccCard uiccCard = phone.getUiccCard();
- if (uiccCard == null) {
+ final UiccPort uiccPort = phone.getUiccPort();
+ if (uiccPort == null) {
Log.e(LOG_TAG,
- "handleSimLock: uiccCard for phone " + phone.getPhoneId() + " is null");
+ "handleSimLock: uiccPort for phone " + phone.getPhoneId() + " is null");
return;
}
- final UiccProfile uiccProfile = uiccCard.getUiccProfile();
+ final UiccProfile uiccProfile = uiccPort.getUiccProfile();
if (uiccProfile == null) {
Log.e(LOG_TAG,
"handleSimLock: uiccProfile for phone " + phone.getPhoneId() + " is null");
@@ -462,6 +464,8 @@
imsRcsController = ImsRcsController.init(this);
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS)) {
+ mImsStateCallbackController =
+ ImsStateCallbackController.make(this, PhoneFactory.getPhones().length);
mTelephonyRcsService = new TelephonyRcsService(this,
PhoneFactory.getPhones().length);
mTelephonyRcsService.initialize();
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 9c078dd..55452f3 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -22,6 +22,7 @@
import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_GSM;
import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_IMS;
import static com.android.internal.telephony.PhoneConstants.SUBSCRIPTION_KEY;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS__EVENT__CLIENT_PARAMS_SENT;
import android.Manifest;
import android.Manifest.permission;
@@ -152,6 +153,7 @@
import com.android.internal.telephony.HalVersion;
import com.android.internal.telephony.IBooleanConsumer;
import com.android.internal.telephony.ICallForwardingInfoCallback;
+import com.android.internal.telephony.IImsStateCallback;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.INumberVerificationCallback;
import com.android.internal.telephony.ITelephony;
@@ -180,6 +182,7 @@
import com.android.internal.telephony.ims.ImsResolver;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
+import com.android.internal.telephony.metrics.RcsStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
import com.android.internal.telephony.uicc.IccIoResult;
@@ -189,6 +192,7 @@
import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.uicc.UiccPort;
import com.android.internal.telephony.uicc.UiccProfile;
import com.android.internal.telephony.uicc.UiccSlot;
import com.android.internal.telephony.util.LocaleUtils;
@@ -503,7 +507,7 @@
MainThreadRequest request;
Message onCompleted;
AsyncResult ar;
- UiccCard uiccCard;
+ UiccPort uiccPort;
IccAPDUArgument iccArgument;
final Phone defaultPhone = getDefaultPhone();
@@ -553,15 +557,15 @@
case CMD_TRANSMIT_APDU_LOGICAL_CHANNEL:
request = (MainThreadRequest) msg.obj;
iccArgument = (IccAPDUArgument) request.argument;
- uiccCard = getUiccCardFromRequest(request);
- if (uiccCard == null) {
+ uiccPort = getUiccPortFromRequest(request);
+ if (uiccPort == null) {
loge("iccTransmitApduLogicalChannel: No UICC");
request.result = new IccIoResult(0x6F, 0, (byte[])null);
notifyRequester(request);
} else {
onCompleted = obtainMessage(EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE,
request);
- uiccCard.iccTransmitApduLogicalChannel(
+ uiccPort.iccTransmitApduLogicalChannel(
iccArgument.channel, iccArgument.cla, iccArgument.command,
iccArgument.p1, iccArgument.p2, iccArgument.p3, iccArgument.data,
onCompleted);
@@ -590,15 +594,15 @@
case CMD_TRANSMIT_APDU_BASIC_CHANNEL:
request = (MainThreadRequest) msg.obj;
iccArgument = (IccAPDUArgument) request.argument;
- uiccCard = getUiccCardFromRequest(request);
- if (uiccCard == null) {
+ uiccPort = getUiccPortFromRequest(request);
+ if (uiccPort == null) {
loge("iccTransmitApduBasicChannel: No UICC");
request.result = new IccIoResult(0x6F, 0, (byte[])null);
notifyRequester(request);
} else {
onCompleted = obtainMessage(EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE,
request);
- uiccCard.iccTransmitApduBasicChannel(
+ uiccPort.iccTransmitApduBasicChannel(
iccArgument.cla, iccArgument.command, iccArgument.p1, iccArgument.p2,
iccArgument.p3, iccArgument.data, onCompleted);
}
@@ -626,15 +630,15 @@
case CMD_EXCHANGE_SIM_IO:
request = (MainThreadRequest) msg.obj;
iccArgument = (IccAPDUArgument) request.argument;
- uiccCard = getUiccCardFromRequest(request);
- if (uiccCard == null) {
+ uiccPort = getUiccPortFromRequest(request);
+ if (uiccPort == null) {
loge("iccExchangeSimIO: No UICC");
request.result = new IccIoResult(0x6F, 0, (byte[])null);
notifyRequester(request);
} else {
onCompleted = obtainMessage(EVENT_EXCHANGE_SIM_IO_DONE,
request);
- uiccCard.iccExchangeSimIO(iccArgument.cla, /* fileID */
+ uiccPort.iccExchangeSimIO(iccArgument.cla, /* fileID */
iccArgument.command, iccArgument.p1, iccArgument.p2, iccArgument.p3,
iccArgument.data, onCompleted);
}
@@ -653,14 +657,14 @@
case CMD_SEND_ENVELOPE:
request = (MainThreadRequest) msg.obj;
- uiccCard = getUiccCardFromRequest(request);
- if (uiccCard == null) {
+ uiccPort = getUiccPortFromRequest(request);
+ if (uiccPort == null) {
loge("sendEnvelopeWithStatus: No UICC");
request.result = new IccIoResult(0x6F, 0, (byte[])null);
notifyRequester(request);
} else {
onCompleted = obtainMessage(EVENT_SEND_ENVELOPE_DONE, request);
- uiccCard.sendEnvelopeWithStatus((String)request.argument, onCompleted);
+ uiccPort.sendEnvelopeWithStatus((String)request.argument, onCompleted);
}
break;
@@ -685,16 +689,16 @@
case CMD_OPEN_CHANNEL:
request = (MainThreadRequest) msg.obj;
- uiccCard = getUiccCardFromRequest(request);
+ uiccPort = getUiccPortFromRequest(request);
Pair<String, Integer> openChannelArgs = (Pair<String, Integer>) request.argument;
- if (uiccCard == null) {
+ if (uiccPort == null) {
loge("iccOpenLogicalChannel: No UICC");
request.result = new IccOpenLogicalChannelResponse(-1,
IccOpenLogicalChannelResponse.STATUS_MISSING_RESOURCE, null);
notifyRequester(request);
} else {
onCompleted = obtainMessage(EVENT_OPEN_CHANNEL_DONE, request);
- uiccCard.iccOpenLogicalChannel(openChannelArgs.first,
+ uiccPort.iccOpenLogicalChannel(openChannelArgs.first,
openChannelArgs.second, onCompleted);
}
break;
@@ -742,14 +746,14 @@
case CMD_CLOSE_CHANNEL:
request = (MainThreadRequest) msg.obj;
- uiccCard = getUiccCardFromRequest(request);
- if (uiccCard == null) {
+ uiccPort = getUiccPortFromRequest(request);
+ if (uiccPort == null) {
loge("iccCloseLogicalChannel: No UICC");
request.result = false;
notifyRequester(request);
} else {
onCompleted = obtainMessage(EVENT_CLOSE_CHANNEL_DONE, request);
- uiccCard.iccCloseLogicalChannel((Integer) request.argument, onCompleted);
+ uiccPort.iccCloseLogicalChannel((Integer) request.argument, onCompleted);
}
break;
@@ -1096,7 +1100,9 @@
// any service for voice call.
if ((callForwardInfo.serviceClass
& CommandsInterface.SERVICE_CLASS_VOICE) > 0) {
- callForwardingInfo = new CallForwardingInfo(true,
+ callForwardingInfo = new CallForwardingInfo(
+ callForwardInfo.status
+ == CommandsInterface.CF_ACTION_ENABLE,
callForwardInfo.reason,
callForwardInfo.number,
callForwardInfo.timeSeconds);
@@ -1461,16 +1467,16 @@
case CMD_GET_FORBIDDEN_PLMNS:
request = (MainThreadRequest) msg.obj;
- uiccCard = getUiccCardFromRequest(request);
- if (uiccCard == null) {
- loge("getForbiddenPlmns() UiccCard is null");
+ uiccPort = getUiccPortFromRequest(request);
+ if (uiccPort == null) {
+ loge("getForbiddenPlmns() UiccPort is null");
request.result = new IllegalArgumentException(
- "getForbiddenPlmns() UiccCard is null");
+ "getForbiddenPlmns() UiccPort is null");
notifyRequester(request);
break;
}
Integer appType = (Integer) request.argument;
- UiccCardApplication uiccApp = uiccCard.getApplicationByType(appType);
+ UiccCardApplication uiccApp = uiccPort.getApplicationByType(appType);
if (uiccApp == null) {
loge("getForbiddenPlmns() no app with specified type -- "
+ appType);
@@ -1766,9 +1772,9 @@
break;
case CMD_SET_FORBIDDEN_PLMNS:
request = (MainThreadRequest) msg.obj;
- uiccCard = getUiccCardFromRequest(request);
- if (uiccCard == null) {
- loge("setForbiddenPlmns: UiccCard is null");
+ uiccPort = getUiccPortFromRequest(request);
+ if (uiccPort == null) {
+ loge("setForbiddenPlmns: UiccPort is null");
request.result = -1;
notifyRequester(request);
break;
@@ -1777,7 +1783,7 @@
(Pair<Integer, List<String>>) request.argument;
appType = setFplmnsArgs.first;
List<String> fplmns = setFplmnsArgs.second;
- uiccApp = uiccCard.getApplicationByType(appType);
+ uiccApp = uiccPort.getApplicationByType(appType);
if (uiccApp == null) {
loge("setForbiddenPlmns: no app with specified type -- " + appType);
request.result = -1;
@@ -1975,7 +1981,7 @@
(Pair<Integer, SignalStrengthUpdateRequest>) request.argument;
onCompleted = obtainMessage(EVENT_SET_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE,
request);
- phone.getServiceStateTracker().setSignalStrengthUpdateRequest(
+ phone.getSignalStrengthController().setSignalStrengthUpdateRequest(
request.subId, pair.first /*callingUid*/,
pair.second /*request*/, onCompleted);
break;
@@ -2003,7 +2009,7 @@
(Pair<Integer, SignalStrengthUpdateRequest>) request.argument;
onCompleted = obtainMessage(EVENT_CLEAR_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE,
request);
- phone.getServiceStateTracker().clearSignalStrengthUpdateRequest(
+ phone.getSignalStrengthController().clearSignalStrengthUpdateRequest(
request.subId, pair.first /*callingUid*/,
pair.second /*request*/, onCompleted);
break;
@@ -2055,7 +2061,8 @@
case CMD_PREPARE_UNATTENDED_REBOOT:
request = (MainThreadRequest) msg.obj;
request.result =
- UiccController.getInstance().getPinStorage().prepareUnattendedReboot();
+ UiccController.getInstance().getPinStorage()
+ .prepareUnattendedReboot(request.workSource);
notifyRequester(request);
break;
@@ -2295,10 +2302,10 @@
? getDefaultPhone() : getPhone(subId);
}
- private UiccCard getUiccCardFromRequest(MainThreadRequest request) {
+ private UiccPort getUiccPortFromRequest(MainThreadRequest request) {
Phone phone = getPhoneFromRequest(request);
return phone == null ? null :
- UiccController.getInstance().getUiccCard(phone.getPhoneId());
+ UiccController.getInstance().getUiccPort(phone.getPhoneId());
}
// returns phone associated with the subId.
@@ -3221,7 +3228,12 @@
String tac = null;
if (phone != null) {
String imei = phone.getImei();
- tac = imei == null ? null : imei.substring(0, TYPE_ALLOCATION_CODE_LENGTH);
+ try {
+ tac = imei == null ? null : imei.substring(0, TYPE_ALLOCATION_CODE_LENGTH);
+ } catch (IndexOutOfBoundsException e) {
+ Log.e(LOG_TAG, "IMEI length shorter than upper index.");
+ return null;
+ }
}
return tac;
}
@@ -3260,7 +3272,13 @@
String manufacturerCode = null;
if (phone != null) {
String meid = phone.getMeid();
- manufacturerCode = meid == null ? null : meid.substring(0, MANUFACTURER_CODE_LENGTH);
+ try {
+ manufacturerCode =
+ meid == null ? null : meid.substring(0, MANUFACTURER_CODE_LENGTH);
+ } catch (IndexOutOfBoundsException e) {
+ Log.e(LOG_TAG, "MEID length shorter than upper index.");
+ return null;
+ }
}
return manufacturerCode;
}
@@ -3382,17 +3400,6 @@
mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
}
- /**
- * Make sure the caller is system.
- *
- * @throws SecurityException if the caller is not system.
- */
- private static void enforceSystemCaller() {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("Caller must be system");
- }
- }
-
private void enforceActiveEmergencySessionPermission() {
mApp.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null);
@@ -5511,11 +5518,9 @@
*/
public int setForbiddenPlmns(int subId, int appType, List<String> fplmns, String callingPackage,
String callingFeatureId) {
- if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage,
- callingFeatureId, "setForbiddenPlmns")) {
- if (DBG) logv("no permissions for setForbiddenplmns");
- throw new IllegalStateException("No Permissions for setForbiddenPlmns");
- }
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "setForbiddenPlmns");
+
if (appType != TelephonyManager.APPTYPE_USIM && appType != TelephonyManager.APPTYPE_SIM) {
loge("setForbiddenPlmnList(): App Type must be USIM or SIM");
throw new IllegalArgumentException("Invalid appType: App Type must be USIM or SIM");
@@ -6576,34 +6581,6 @@
}
/**
- * Enable or disable always reporting signal strength changes from radio.
- *
- * @param isEnable {@code true} for enabling; {@code false} for disabling.
- */
- @Override
- public void setAlwaysReportSignalStrength(int subId, boolean isEnable) {
- enforceModifyPermission();
- enforceSystemCaller();
-
- final long identity = Binder.clearCallingIdentity();
- final Phone phone = getPhone(subId);
- try {
- if (phone != null) {
- if (DBG) {
- log("setAlwaysReportSignalStrength: subId=" + subId
- + " isEnable=" + isEnable);
- }
- phone.setAlwaysReportSignalStrength(isEnable);
- } else {
- loge("setAlwaysReportSignalStrength: no phone found for subId="
- + subId);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
* Get the user enabled state of Mobile Data.
*
* TODO: remove and use isUserDataEnabled.
@@ -6813,14 +6790,14 @@
loge("getCarrierPrivilegeStatus: Invalid subId");
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
}
- UiccCard card = UiccController.getInstance().getUiccCard(phone.getPhoneId());
- if (card == null) {
+ UiccPort port = UiccController.getInstance().getUiccPort(phone.getPhoneId());
+ if (port == null) {
loge("getCarrierPrivilegeStatus: No UICC");
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
}
return getCarrierPrivilegeStatusFromCarrierConfigRules(
- card.getCarrierPrivilegeStatusForCurrentTransaction(
+ port.getCarrierPrivilegeStatusForCurrentTransaction(
phone.getContext().getPackageManager()), Binder.getCallingUid(), phone);
}
@@ -6851,13 +6828,13 @@
}
int phoneId = SubscriptionManager.getPhoneId(subId);
- UiccCard card = UiccController.getInstance().getUiccCard(phoneId);
- if (card == null) {
+ UiccPort port = UiccController.getInstance().getUiccPort(phoneId);
+ if (port == null) {
loge("checkCarrierPrivilegesForPackage: No UICC on subId " + subId);
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
}
return getCarrierPrivilegeStatusFromCarrierConfigRules(
- card.getCarrierPrivilegeStatus(mApp.getPackageManager(), pkgName),
+ port.getCarrierPrivilegeStatus(mApp.getPackageManager(), pkgName),
getPhone(phoneId), pkgName);
}
@@ -6868,14 +6845,14 @@
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
int result = TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
- UiccCard card = UiccController.getInstance().getUiccCard(i);
- if (card == null) {
+ UiccPort port = UiccController.getInstance().getUiccPort(i);
+ if (port == null) {
// No UICC in that slot.
continue;
}
result = getCarrierPrivilegeStatusFromCarrierConfigRules(
- card.getCarrierPrivilegeStatus(mApp.getPackageManager(), pkgName),
+ port.getCarrierPrivilegeStatus(mApp.getPackageManager(), pkgName),
getPhone(i), pkgName);
if (result == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
break;
@@ -6892,12 +6869,12 @@
loge("phoneId " + phoneId + " is not valid.");
return null;
}
- UiccCard card = UiccController.getInstance().getUiccCard(phoneId);
- if (card == null) {
+ UiccPort port = UiccController.getInstance().getUiccPort(phoneId);
+ if (port == null) {
loge("getCarrierPackageNamesForIntentAndPhone: No UICC");
return null ;
}
- return card.getCarrierPackageNamesForIntent(mApp.getPackageManager(), intent);
+ return port.getCarrierPackageNamesForIntent(mApp.getPackageManager(), intent);
}
@Override
@@ -6906,10 +6883,10 @@
PackageManager pm = mApp.getPackageManager();
List<String> privilegedPackages = new ArrayList<>();
List<PackageInfo> packages = null;
- UiccCard card = UiccController.getInstance().getUiccCard(phoneId);
+ UiccPort port = UiccController.getInstance().getUiccPort(phoneId);
// has UICC in that slot.
- if (card != null) {
- if (card.hasCarrierPrivilegeRules()) {
+ if (port != null) {
+ if (port.hasCarrierPrivilegeRules()) {
if (packages == null) {
// Only check packages in user 0 for now
packages = pm.getInstalledPackagesAsUser(
@@ -6921,7 +6898,9 @@
for (int p = packages.size() - 1; p >= 0; p--) {
PackageInfo pkgInfo = packages.get(p);
if (pkgInfo != null && pkgInfo.packageName != null
- && card.getCarrierPrivilegeStatus(pkgInfo)
+ && getCarrierPrivilegeStatusFromCarrierConfigRules(
+ port.getCarrierPrivilegeStatus(pkgInfo),
+ getPhone(phoneId), pkgInfo.packageName)
== TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
privilegedPackages.add(pkgInfo.packageName);
}
@@ -6950,11 +6929,11 @@
private String getIccId(int subId) {
final Phone phone = getPhone(subId);
- UiccCard card = phone == null ? null : phone.getUiccCard();
- if (card == null) {
+ UiccPort port = phone == null ? null : phone.getUiccPort();
+ if (port == null) {
return null;
}
- String iccId = card.getIccId();
+ String iccId = port.getIccId();
if (TextUtils.isEmpty(iccId)) {
return null;
}
@@ -8122,7 +8101,7 @@
}
String aid = null;
try {
- aid = UiccController.getInstance().getUiccCard(phone.getPhoneId())
+ aid = UiccController.getInstance().getUiccPort(phone.getPhoneId())
.getApplicationByType(appType).getAid();
} catch (Exception e) {
Log.e(LOG_TAG, "Not getting aid. Exception ex=" + e);
@@ -8311,6 +8290,16 @@
int result = (int) sendRequest(CMD_ENABLE_VONR, enabled, subId,
workSource);
if (DBG) log("setVoNrEnabled result: " + result);
+
+ if (result == TelephonyManager.ENABLE_VONR_SUCCESS) {
+ if (DBG) {
+ log("Set VoNR settings in siminfo db; subId=" + subId + ", value:" + enabled);
+ }
+ SubscriptionManager.setSubscriptionProperty(
+ subId, SubscriptionManager.NR_ADVANCED_CALLING_ENABLED,
+ (enabled ? "1" : "0"));
+ }
+
return result;
} finally {
Binder.restoreCallingIdentity(identity);
@@ -8699,6 +8688,22 @@
return isAllowed;
}
+ private boolean haveCarrierPrivilegeAccess(UiccCard card, String callingPackage) {
+ // TODO once MEP API refactoring CL is merged, loop port list from UiccCardInfo,
+ // and if find the matching UiccPort by UiccController.getUiccPortForSlot(slot, portIdx)
+ // Update each UiccPort object based on privilege access
+ UiccPort[] uiccPorts = card.getUiccPortList();
+ for (UiccPort port : uiccPorts) {
+ UiccProfile profile = port.getUiccProfile();
+ if (profile == null ||
+ profile.getCarrierPrivilegeStatus(mApp.getPackageManager(), callingPackage)
+ != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ return false;
+ }
+ }
+ return true;
+ }
+
@Override
public List<UiccCardInfo> getUiccCardsInfo(String callingPackage) {
// Verify that tha callingPackage belongs to the calling UID
@@ -8732,14 +8737,15 @@
// For an inactive eUICC, the UiccCard will be null even though the UiccCardInfo
// is available
UiccCard card = uiccController.getUiccCardForSlot(cardInfo.getSlotIndex());
- if (card == null || card.getUiccProfile() == null) {
- // assume no access if the card or profile is unavailable
+ // TODO remove card.getUiccPortList().length once MEP API refactoring CL is merged
+ // Get UiccPortInfo from CardInfo and process further based on each UiccPort
+ if (card == null || card.getUiccPortList().length == 0) {
+ // assume no access if the card or ports are unavailable
filteredInfos.add(cardInfo.getUnprivileged());
continue;
}
- UiccProfile profile = card.getUiccProfile();
- if (profile.getCarrierPrivilegeStatus(mApp.getPackageManager(), callingPackage)
- == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+
+ if (haveCarrierPrivilegeAccess(card, callingPackage)) {
filteredInfos.add(cardInfo);
} else {
filteredInfos.add(cardInfo.getUnprivileged());
@@ -8860,11 +8866,11 @@
if (phone == null) {
return;
}
- UiccCard uiccCard = phone.getUiccCard();
- if (uiccCard == null) {
+ UiccPort uiccPort = phone.getUiccPort();
+ if (uiccPort == null) {
return;
}
- UiccProfile uiccProfile = uiccCard.getUiccProfile();
+ UiccProfile uiccProfile = uiccPort.getUiccProfile();
if (uiccProfile == null) {
return;
}
@@ -9371,11 +9377,11 @@
}
final long identity = Binder.clearCallingIdentity();
try {
- UiccCard uiccCard = phone.getUiccCard();
- if (uiccCard == null) {
+ UiccPort uiccPort = phone.getUiccPort();
+ if (uiccPort == null) {
return false;
}
- UiccProfile uiccProfile = uiccCard.getUiccProfile();
+ UiccProfile uiccProfile = uiccPort.getUiccProfile();
if (uiccProfile == null) {
return false;
}
@@ -10379,6 +10385,9 @@
} else {
configBinder.setRcsClientConfiguration(rcc);
}
+
+ RcsStats.getInstance().onRcsClientProvisioningStats(subId,
+ RCS_CLIENT_PROVISIONING_STATS__EVENT__CLIENT_PARAMS_SENT);
} catch (RemoteException e) {
Rlog.e(LOG_TAG, "fail to setRcsClientConfiguration " + e.getMessage());
throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
@@ -10781,7 +10790,7 @@
mApp.getSystemService(AppOpsManager.class)
.checkPackage(callingUid, callingPackage);
- validateSignalStrengthUpdateRequest(request, callingUid);
+ validateSignalStrengthUpdateRequest(mApp, request, callingUid);
final long identity = Binder.clearCallingIdentity();
try {
@@ -10820,19 +10829,19 @@
}
}
- private static void validateSignalStrengthUpdateRequest(SignalStrengthUpdateRequest request,
- int callingUid) {
+ private static void validateSignalStrengthUpdateRequest(Context context,
+ SignalStrengthUpdateRequest request, int callingUid) {
if (callingUid == Process.PHONE_UID || callingUid == Process.SYSTEM_UID) {
// phone/system process do not have further restriction on request
return;
}
// Applications has restrictions on how to use the request:
- // Only system caller can set mIsSystemThresholdReportingRequestedWhileIdle
+ // Non-system callers need permission to set mIsSystemThresholdReportingRequestedWhileIdle
if (request.isSystemThresholdReportingRequestedWhileIdle()) {
- // This is not system caller which has been checked above
- throw new IllegalArgumentException(
- "Only system can set isSystemThresholdReportingRequestedWhileIdle");
+ context.enforceCallingOrSelfPermission(
+ android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH,
+ "validateSignalStrengthUpdateRequest");
}
for (SignalThresholdInfo info : request.getSignalThresholdInfos()) {
@@ -10884,11 +10893,12 @@
@Override
@TelephonyManager.PrepareUnattendedRebootResult
public int prepareForUnattendedReboot() {
+ WorkSource workSource = getWorkSource(Binder.getCallingUid());
enforceRebootPermission();
final long identity = Binder.clearCallingIdentity();
try {
- return (int) sendRequest(CMD_PREPARE_UNATTENDED_REBOOT, null);
+ return (int) sendRequest(CMD_PREPARE_UNATTENDED_REBOOT, null, workSource);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -10912,4 +10922,68 @@
Binder.restoreCallingIdentity(identity);
}
}
+
+ /**
+ * Register an IMS connection state callback
+ */
+ @Override
+ public void registerImsStateCallback(int subId, int feature, IImsStateCallback cb) {
+ if (feature == ImsFeature.FEATURE_MMTEL) {
+ // ImsMmTelManager
+ // The following also checks READ_PRIVILEGED_PHONE_STATE.
+ TelephonyPermissions
+ .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "registerImsStateCallback");
+ } else if (feature == ImsFeature.FEATURE_RCS) {
+ // ImsRcsManager or SipDelegateManager
+ TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges(mApp, subId,
+ Binder.getCallingUid(), "registerImsStateCallback",
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ Manifest.permission.READ_PRECISE_PHONE_STATE,
+ Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE,
+ Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION);
+ }
+
+ if (!ImsManager.isImsSupportedOnDevice(mApp)) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "IMS not available on device.");
+ }
+
+ if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION);
+ }
+
+ ImsStateCallbackController controller = ImsStateCallbackController.getInstance();
+ if (controller == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "IMS not available on device.");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ int slotId = getSlotIndexOrException(subId);
+ controller.registerImsStateCallback(subId, feature, cb);
+ } catch (ImsException e) {
+ throw new ServiceSpecificException(e.getCode());
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Unregister an IMS connection state callback
+ */
+ @Override
+ public void unregisterImsStateCallback(IImsStateCallback cb) {
+ final long token = Binder.clearCallingIdentity();
+ ImsStateCallbackController controller = ImsStateCallbackController.getInstance();
+ if (controller == null) {
+ return;
+ }
+ try {
+ controller.unregisterImsStateCallback(cb);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
}
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index c520063..35cfdd3 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -468,7 +468,8 @@
};
// build the dialog
- final AlertDialog newDialog = new AlertDialog.Builder(contextThemeWrapper)
+ final AlertDialog newDialog =
+ FrameworksUtils.makeAlertDialogBuilder(contextThemeWrapper)
.setMessage(text)
.setView(dialogView)
.setPositiveButton(R.string.send_button, mUSSDDialogListener)
diff --git a/src/com/android/phone/RcsProvisioningMonitor.java b/src/com/android/phone/RcsProvisioningMonitor.java
index 23c4c5a..e819afc 100644
--- a/src/com/android/phone/RcsProvisioningMonitor.java
+++ b/src/com/android/phone/RcsProvisioningMonitor.java
@@ -16,6 +16,10 @@
package com.android.phone;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS__EVENT__DMA_CHANGED;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS__EVENT__TRIGGER_RCS_RECONFIGURATION;
+
import android.Manifest;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
@@ -47,6 +51,8 @@
import com.android.ims.FeatureUpdates;
import com.android.ims.RcsFeatureManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.metrics.RcsStats;
+import com.android.internal.telephony.metrics.RcsStats.RcsProvisioningCallback;
import com.android.internal.telephony.util.HandlerExecutor;
import com.android.internal.util.CollectionUtils;
import com.android.telephony.Rlog;
@@ -75,6 +81,10 @@
private static final int EVENT_RESET = 8;
private static final int EVENT_FEATURE_ENABLED_OVERRIDE = 9;
+ // indicate that the carrier single registration capable is initial value as
+ // carrier config is not ready yet.
+ private static final int MASK_CAP_CARRIER_INIT = 0xF000;
+
private final PhoneGlobals mPhone;
private final Handler mHandler;
// Cache the RCS provsioning info and related sub id
@@ -96,6 +106,8 @@
private final RoleManagerAdapter mRoleManager;
private FeatureConnectorFactory<RcsFeatureManager> mFeatureFactory;
+ private RcsStats mRcsStats;
+
private static RcsProvisioningMonitor sInstance;
private final SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener =
@@ -220,7 +232,23 @@
}
void setSingleRegistrationCapability(int singleRegistrationCapability) {
- mSingleRegistrationCapability = singleRegistrationCapability;
+ if (mSingleRegistrationCapability != singleRegistrationCapability) {
+ mSingleRegistrationCapability = singleRegistrationCapability;
+ notifyDma();
+
+ // update whether single registration supported.
+ mRcsStats.setEnableSingleRegistration(mSubId,
+ mSingleRegistrationCapability == ProvisioningManager.STATUS_CAPABLE);
+ }
+ }
+
+ void notifyDma() {
+ // notify only if capable value has been updated when carrier config ready.
+ if ((mSingleRegistrationCapability & MASK_CAP_CARRIER_INIT) != MASK_CAP_CARRIER_INIT) {
+ logi("notify default messaging app for sub:" + mSubId + " with capability:"
+ + mSingleRegistrationCapability);
+ notifyDmaForSub(mSubId, mSingleRegistrationCapability);
+ }
}
int getSingleRegistrationCapability() {
@@ -322,6 +350,9 @@
} else {
notifyRcsAutoConfigurationReceived();
}
+
+ // check callback for metrics if not registered, register callback
+ registerMetricsCallback();
} else {
// clear callbacks if rcs disconnected
clearCallbacks();
@@ -381,6 +412,18 @@
}
}
}
+
+ private void registerMetricsCallback() {
+ RcsProvisioningCallback rcsProvisioningCallback = mRcsStats.getRcsProvisioningCallback(
+ mSubId, mSingleRegistrationCapability == ProvisioningManager.STATUS_CAPABLE);
+
+ // if not yet registered, register callback and set registered value
+ if (rcsProvisioningCallback != null && !rcsProvisioningCallback.getRegistered()) {
+ if (addRcsConfigCallback(rcsProvisioningCallback)) {
+ rcsProvisioningCallback.setRegistered(true);
+ }
+ }
+ }
}
@VisibleForTesting
@@ -438,7 +481,7 @@
@VisibleForTesting
public RcsProvisioningMonitor(PhoneGlobals app, Looper looper, RoleManagerAdapter roleManager,
- FeatureConnectorFactory<RcsFeatureManager> factory) {
+ FeatureConnectorFactory<RcsFeatureManager> factory, RcsStats rcsStats) {
mPhone = app;
mHandler = new MyHandler(looper);
mCarrierConfigManager = mPhone.getSystemService(CarrierConfigManager.class);
@@ -449,6 +492,7 @@
logv("DMA is " + mDmaPackageName);
mDmaChangedListener = new DmaChangedListener();
mFeatureFactory = factory;
+ mRcsStats = rcsStats;
init();
}
@@ -461,7 +505,8 @@
HandlerThread handlerThread = new HandlerThread(TAG);
handlerThread.start();
sInstance = new RcsProvisioningMonitor(app, handlerThread.getLooper(),
- new RoleManagerAdapterImpl(app), RcsFeatureManager::getConnector);
+ new RoleManagerAdapterImpl(app), RcsFeatureManager::getConnector,
+ RcsStats.getInstance());
}
return sInstance;
}
@@ -675,7 +720,7 @@
logv("new default messaging application " + mDmaPackageName);
mRcsProvisioningInfos.forEach((k, v) -> {
- notifyDmaForSub(k, v.getSingleRegistrationCapability());
+ v.notifyDma();
byte[] cachedConfig = v.getConfig();
//clear old callbacks
@@ -688,6 +733,10 @@
logv("acs not used, set cached config and notify.");
v.setConfig(cachedConfig);
}
+
+ // store RCS metrics - DMA changed event
+ mRcsStats.onRcsClientProvisioningStats(k,
+ RCS_CLIENT_PROVISIONING_STATS__EVENT__DMA_CHANGED);
});
}
}
@@ -715,17 +764,20 @@
return b.getBoolean(CarrierConfigManager.KEY_USE_ACS_FOR_RCS_BOOL);
}
- private boolean isSingleRegistrationRequiredByCarrier(int subId) {
+ private int getSingleRegistrationRequiredByCarrier(int subId) {
Boolean enabledByOverride = mCarrierSingleRegistrationEnabledOverride.get(subId);
if (enabledByOverride != null) {
- return enabledByOverride;
+ return enabledByOverride ? ProvisioningManager.STATUS_CAPABLE
+ : ProvisioningManager.STATUS_CARRIER_NOT_CAPABLE;
}
PersistableBundle b = mCarrierConfigManager.getConfigForSubId(subId);
- if (b == null) {
- return false;
+ if (!CarrierConfigManager.isConfigForIdentifiedCarrier(b)) {
+ return MASK_CAP_CARRIER_INIT;
}
- return b.getBoolean(CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL);
+ return b.getBoolean(CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL)
+ ? ProvisioningManager.STATUS_CAPABLE
+ : ProvisioningManager.STATUS_CARRIER_NOT_CAPABLE;
}
private int getSingleRegistrationCapableValue(int subId) {
@@ -735,10 +787,9 @@
: mPhone.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION);
- int value = (isSingleRegistrationEnabledOnDevice ? 0
- : ProvisioningManager.STATUS_DEVICE_NOT_CAPABLE) | (
- isSingleRegistrationRequiredByCarrier(subId) ? 0
- : ProvisioningManager.STATUS_CARRIER_NOT_CAPABLE);
+ int value = (isSingleRegistrationEnabledOnDevice ? ProvisioningManager.STATUS_CAPABLE
+ : ProvisioningManager.STATUS_DEVICE_NOT_CAPABLE)
+ | getSingleRegistrationRequiredByCarrier(subId);
logv("SingleRegistrationCapableValue : " + value);
return value;
}
@@ -746,11 +797,8 @@
private void onCarrierConfigChange() {
logv("onCarrierConfigChange");
mRcsProvisioningInfos.forEach((subId, info) -> {
- int value = getSingleRegistrationCapableValue(subId);
- if (value != info.getSingleRegistrationCapability()) {
- info.setSingleRegistrationCapability(value);
- notifyDmaForSub(subId, value);
- }
+ info.setSingleRegistrationCapability(
+ getSingleRegistrationCapableValue(subId));
});
}
@@ -765,9 +813,8 @@
byte[] data = loadConfigForSub(i);
int capability = getSingleRegistrationCapableValue(i);
logv("new info is created for sub : " + i + ", single registration capability :"
- + capability + ", rcs config : " + data);
+ + capability + ", rcs config : " + Arrays.toString(data));
mRcsProvisioningInfos.put(i, new RcsProvisioningInfo(i, capability, data));
- notifyDmaForSub(i, capability);
}
}
@@ -783,8 +830,20 @@
logv("onConfigReceived, subId:" + subId + ", config:"
+ config + ", isCompressed:" + isCompressed);
RcsProvisioningInfo info = mRcsProvisioningInfos.get(subId);
+ if (info == null) {
+ logd("sub[" + subId + "] has been removed");
+ return;
+ }
info.setConfig(isCompressed ? RcsConfig.decompressGzip(config) : config);
updateConfigForSub(subId, config, isCompressed);
+
+ // Supporting ACS means config data comes from ACS
+ // store RCS metrics - received provisioning event
+ if (isAcsUsed(subId)) {
+ mRcsStats.onRcsAcsProvisioningStats(subId, 200,
+ RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML,
+ isRcsVolteSingleRegistrationEnabled(subId));
+ }
}
private void onReconfigRequest(int subId) {
@@ -796,6 +855,10 @@
updateConfigForSub(subId, null, true);
info.triggerRcsReconfiguration();
}
+
+ // store RCS metrics - reconfig event
+ mRcsStats.onRcsClientProvisioningStats(subId,
+ RCS_CLIENT_PROVISIONING_STATS__EVENT__TRIGGER_RCS_RECONFIGURATION);
}
private void notifyDmaForSub(int subId, int capability) {
@@ -852,6 +915,10 @@
}
}
+ private static void logi(String msg) {
+ Rlog.i(TAG, msg);
+ }
+
private static void logd(String msg) {
Rlog.d(TAG, msg);
}
diff --git a/src/com/android/phone/SimPhonebookProvider.java b/src/com/android/phone/SimPhonebookProvider.java
index 04c4f48..8952865 100644
--- a/src/com/android/phone/SimPhonebookProvider.java
+++ b/src/com/android/phone/SimPhonebookProvider.java
@@ -43,7 +43,7 @@
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.TelephonyManager;
import android.util.ArraySet;
-import android.util.Pair;
+import android.util.SparseArray;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -55,10 +55,10 @@
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.MoreExecutors;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
@@ -333,19 +333,25 @@
if (recordsSize == null || getRecordCount(recordsSize) == 0) {
return;
}
+ int efid = efIdForEfType(efType);
+ // Have to load the existing records to get the size because there may be more than one
+ // phonebook set in which case the total capacity is the sum of the capacity of EF_ADN for
+ // all the phonebook sets whereas the recordsSize is just the size for a single EF.
+ List<AdnRecord> existingRecords = mIccPhoneBookSupplier.get()
+ .getAdnRecordsInEfForSubscriber(subscriptionInfo.getSubscriptionId(), efid);
+ if (existingRecords == null) {
+ existingRecords = ImmutableList.of();
+ }
MatrixCursor.RowBuilder row = result.newRow()
.add(ElementaryFiles.SLOT_INDEX, subscriptionInfo.getSimSlotIndex())
.add(ElementaryFiles.SUBSCRIPTION_ID, subscriptionInfo.getSubscriptionId())
.add(ElementaryFiles.EF_TYPE, efType)
- .add(ElementaryFiles.MAX_RECORDS, getRecordCount(recordsSize))
+ .add(ElementaryFiles.MAX_RECORDS, existingRecords.size())
.add(ElementaryFiles.NAME_MAX_LENGTH,
AdnRecord.getMaxAlphaTagBytes(getRecordSize(recordsSize)))
.add(ElementaryFiles.PHONE_NUMBER_MAX_LENGTH,
AdnRecord.getMaxPhoneNumberDigits());
if (result.getColumnIndex(ElementaryFiles.RECORD_COUNT) != -1) {
- int efid = efIdForEfType(efType);
- List<AdnRecord> existingRecords = mIccPhoneBookSupplier.get()
- .getAdnRecordsInEfForSubscriber(subscriptionInfo.getSubscriptionId(), efid);
int nonEmptyCount = 0;
for (AdnRecord record : existingRecords) {
if (!record.isEmpty()) {
@@ -368,39 +374,46 @@
return new MatrixCursor(projection, 0);
}
MatrixCursor result = new MatrixCursor(projection, records.size());
- List<Pair<AdnRecord, MatrixCursor.RowBuilder>> rowBuilders = new ArrayList<>(
- records.size());
- for (AdnRecord record : records) {
+ SparseArray<MatrixCursor.RowBuilder> rowBuilders = new SparseArray<>(records.size());
+ for (int i = 0; i < records.size(); i++) {
+ AdnRecord record = records.get(i);
if (!record.isEmpty()) {
- rowBuilders.add(Pair.create(record, result.newRow()));
+ rowBuilders.put(i, result.newRow());
}
}
// This is kind of ugly but avoids looking up columns in an inner loop.
for (String column : projection) {
switch (column) {
case SimRecords.SUBSCRIPTION_ID:
- for (Pair<AdnRecord, MatrixCursor.RowBuilder> row : rowBuilders) {
- row.second.add(args.subscriptionId);
+ for (int i = 0; i < rowBuilders.size(); i++) {
+ rowBuilders.valueAt(i).add(args.subscriptionId);
}
break;
case SimRecords.ELEMENTARY_FILE_TYPE:
- for (Pair<AdnRecord, MatrixCursor.RowBuilder> row : rowBuilders) {
- row.second.add(args.efType);
+ for (int i = 0; i < rowBuilders.size(); i++) {
+ rowBuilders.valueAt(i).add(args.efType);
}
break;
case SimRecords.RECORD_NUMBER:
- for (Pair<AdnRecord, MatrixCursor.RowBuilder> row : rowBuilders) {
- row.second.add(row.first.getRecId());
+ for (int i = 0; i < rowBuilders.size(); i++) {
+ int index = rowBuilders.keyAt(i);
+ MatrixCursor.RowBuilder rowBuilder = rowBuilders.valueAt(i);
+ // See b/201685690. The logical record number, i.e. the 1-based index in the
+ // list, is used the rather than AdnRecord.getRecId() because getRecId is
+ // not offset when a single logical EF is made up of multiple physical EFs.
+ rowBuilder.add(index + 1);
}
break;
case SimRecords.NAME:
- for (Pair<AdnRecord, MatrixCursor.RowBuilder> row : rowBuilders) {
- row.second.add(row.first.getAlphaTag());
+ for (int i = 0; i < rowBuilders.size(); i++) {
+ AdnRecord record = records.get(rowBuilders.keyAt(i));
+ rowBuilders.valueAt(i).add(record.getAlphaTag());
}
break;
case SimRecords.PHONE_NUMBER:
- for (Pair<AdnRecord, MatrixCursor.RowBuilder> row : rowBuilders) {
- row.second.add(row.first.getNumber());
+ for (int i = 0; i < rowBuilders.size(); i++) {
+ AdnRecord record = records.get(rowBuilders.keyAt(i));
+ rowBuilders.valueAt(i).add(record.getNumber());
}
break;
default:
@@ -765,20 +778,9 @@
if (records == null || args.recordNumber > records.size()) {
return null;
}
- AdnRecord result = records.get(args.recordNumber - 1);
- // This should be true but the service could have a different implementation.
- if (result.getRecId() == args.recordNumber) {
- return result;
- }
- for (AdnRecord record : records) {
- if (record.getRecId() == args.recordNumber) {
- return result;
- }
- }
- return null;
+ return records.get(args.recordNumber - 1);
}
-
private int[] getRecordsSizeForEf(PhonebookArgs args) {
try {
return mIccPhoneBookSupplier.get().getAdnRecordsSizeForSubscriber(
diff --git a/src/com/android/phone/SpecialCharSequenceMgr.java b/src/com/android/phone/SpecialCharSequenceMgr.java
index 674449e..3bf0e1a 100644
--- a/src/com/android/phone/SpecialCharSequenceMgr.java
+++ b/src/com/android/phone/SpecialCharSequenceMgr.java
@@ -264,7 +264,7 @@
}
return isMMIHandled;
} else {
- AlertDialog dialog = new AlertDialog.Builder(context)
+ AlertDialog dialog = FrameworksUtils.makeAlertDialogBuilder(context)
.setMessage(R.string.pin_puk_system_user_only)
.setPositiveButton(R.string.ok, null)
.setCancelable(true).create();
@@ -294,7 +294,7 @@
int labelId = TelephonyCapabilities.getDeviceIdLabel(phone);
String deviceId = phone.getDeviceId();
- AlertDialog alert = new AlertDialog.Builder(context)
+ AlertDialog alert = FrameworksUtils.makeAlertDialogBuilder(context)
.setTitle(labelId)
.setMessage(deviceId)
.setPositiveButton(R.string.ok, null)
diff --git a/src/com/android/phone/TimeConsumingPreferenceActivity.java b/src/com/android/phone/TimeConsumingPreferenceActivity.java
index 8c5ae6d..caef176 100644
--- a/src/com/android/phone/TimeConsumingPreferenceActivity.java
+++ b/src/com/android/phone/TimeConsumingPreferenceActivity.java
@@ -84,7 +84,7 @@
|| id == FDN_CHECK_FAILURE || id == STK_CC_SS_TO_DIAL_ERROR
|| id == STK_CC_SS_TO_USSD_ERROR || id == STK_CC_SS_TO_SS_ERROR
|| id == STK_CC_SS_TO_DIAL_VIDEO_ERROR) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ AlertDialog.Builder builder = FrameworksUtils.makeAlertDialogBuilder(this);
int msgId;
int titleId = R.string.error_updating_title;
diff --git a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
index 224a1f9..6bc71dc 100644
--- a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
+++ b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
@@ -78,6 +78,9 @@
new SubscriptionManager.OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
+ if (getActivity() == null) {
+ return;
+ }
updateAccounts();
}
};
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index 9c89e9e..9321e1e 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -787,12 +787,12 @@
default:
break;
}
- return resourceId == null ? "" : context.getResources().getText(resourceId);
+ return resourceId == null ? "" : context.getResources().getString(resourceId);
}
private static boolean isRadioOffForThermalMitigation(int phoneId) {
Phone phone = PhoneFactory.getPhone(phoneId);
- return phone.isRadioOffForThermalMitigation();
+ return phone == null ? false : phone.isRadioOffForThermalMitigation();
}
/**
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 0be927a..c62b4fa 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -257,7 +257,7 @@
@Override
public void onConnectionPropertiesChanged(Connection c, int connectionProperties) {
- Log.d(this, "onConnectionPropertiesChanged: Connection: %s,"
+ Log.i(ImsConference.this, "onConnectionPropertiesChanged: Connection: %s,"
+ " connectionProperties: %s", c, connectionProperties);
updateConnectionProperties(connectionProperties);
}
@@ -383,6 +383,11 @@
private boolean mIsUsingSimCallManager = false;
/**
+ * See {@link #isRemotelyHosted()} for details.
+ */
+ private boolean mWasRemotelyHosted = false;
+
+ /**
* Where {@link #isMultiparty()} is {@code false}, contains the
* {@link ConferenceParticipantConnection#getUserEntity()} and
* {@link ConferenceParticipantConnection#getEndpoint()} of the single participant which this
@@ -522,11 +527,16 @@
(properties & Connection.PROPERTY_IS_EXTERNAL_CALL) != 0);
conferenceProperties = changeBitmask(conferenceProperties,
- Connection.PROPERTY_REMOTELY_HOSTED, !isConferenceHost());
+ Connection.PROPERTY_REMOTELY_HOSTED, isRemotelyHosted());
conferenceProperties = changeBitmask(conferenceProperties,
Connection.PROPERTY_IS_ADHOC_CONFERENCE,
(properties & Connection.PROPERTY_IS_ADHOC_CONFERENCE) != 0);
+ Log.i(this, "applyHostProperties: confProp=%s", conferenceProperties);
+
+ conferenceProperties = changeBitmask(conferenceProperties,
+ Connection.PROPERTY_CROSS_SIM,
+ (properties & Connection.PROPERTY_CROSS_SIM) != 0);
return conferenceProperties;
}
@@ -774,6 +784,26 @@
}
/**
+ * Returns whether the conference is remotely hosted or not.
+ * This method will cache the current remotely hosted state when the conference host or
+ * original connection becomes null. This is important for scenarios where the conference host
+ * or original connection changes midway through a conference such as in an SRVCC scenario.
+ * @return {@code true} if the conference was remotely hosted based on the conference host and
+ * its original connection, or based on the last known remotely hosted state. {@code false}
+ * otherwise.
+ */
+ public boolean isRemotelyHosted() {
+ if (mConferenceHost == null || mConferenceHost.getOriginalConnection() == null) {
+ return mWasRemotelyHosted;
+ }
+ com.android.internal.telephony.Connection originalConnection =
+ mConferenceHost.getOriginalConnection();
+ mWasRemotelyHosted = originalConnection.isMultiparty()
+ && !originalConnection.isConferenceHost();
+ return mWasRemotelyHosted;
+ }
+
+ /**
* Determines if this conference is hosted on the current device or the peer device.
*
* @return {@code true} if this conference is hosted on the current device, {@code false} if it
@@ -1209,6 +1239,7 @@
ConferenceParticipantConnection connection = new ConferenceParticipantConnection(
parent.getOriginalConnection(), participant,
!isConferenceHost() /* isRemotelyHosted */);
+
if (participant.getConnectTime() == 0) {
connection.setConnectTimeMillis(parent.getConnectTimeMillis());
connection.setConnectionStartElapsedRealtimeMillis(
diff --git a/src/com/android/services/telephony/TelephonyConferenceController.java b/src/com/android/services/telephony/TelephonyConferenceController.java
index 228541a..9aa3dbe 100644
--- a/src/com/android/services/telephony/TelephonyConferenceController.java
+++ b/src/com/android/services/telephony/TelephonyConferenceController.java
@@ -40,6 +40,7 @@
*/
final class TelephonyConferenceController {
private static final int TELEPHONY_CONFERENCE_MAX_SIZE = 5;
+ private static final String RIL_REPORTED_CONFERENCE_CALL_STRING = "Conference Call";
private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
new TelephonyConnection.TelephonyConnectionListener() {
@@ -179,7 +180,6 @@
private void recalculateConference() {
Set<TelephonyConnection> conferencedConnections = new HashSet<>();
int numGsmConnections = 0;
-
for (TelephonyConnection connection : mTelephonyConnections) {
com.android.internal.telephony.Connection radioConnection =
connection.getOriginalConnection();
@@ -271,11 +271,19 @@
// Remove all instances of PROPERTY_IS_DOWNGRADED_CONFERENCE. This
// property should only be set on the parent call (i.e. the newly
// created TelephonyConference.
- Log.d(this, "Removing PROPERTY_IS_DOWNGRADED_CONFERENCE from connection"
- + " %s", connection);
- int newProperties = connection.getConnectionProperties()
- & ~Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE;
- connection.setTelephonyConnectionProperties(newProperties);
+ // This doesn't apply to a connection whose address is "Conference
+ // Call", which may be updated by some modem to create a connection
+ // to represent a merged conference connection in SRVCC.
+ if (connection.getAddress() == null
+ || !connection.getAddress().getSchemeSpecificPart()
+ .equalsIgnoreCase(
+ RIL_REPORTED_CONFERENCE_CALL_STRING)) {
+ Log.d(this, "Removing PROPERTY_IS_DOWNGRADED_CONFERENCE"
+ + " from connection %s", connection);
+ int newProperties = connection.getConnectionProperties()
+ & ~Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE;
+ connection.setTelephonyConnectionProperties(newProperties);
+ }
isDowngradedConference = true;
}
mTelephonyConference.addTelephonyConnection(connection);
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
old mode 100755
new mode 100644
index 53e923e..ed07726
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -2707,6 +2707,10 @@
capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
isLocalVideoSupported);
+ capabilities = changeBitmask(capabilities, CAPABILITY_REMOTE_PARTY_SUPPORTS_RTT,
+ (mOriginalConnectionCapabilities & Capability.SUPPORTS_RTT_REMOTE)
+ == Capability.SUPPORTS_RTT_REMOTE);
+
return capabilities;
}
@@ -3810,4 +3814,13 @@
mCommunicator.sendMessages(set);
}
}
+
+ /**
+ * Returns the current telephony connection listeners for test purposes.
+ * @return list of telephony connection listeners.
+ */
+ @VisibleForTesting
+ public List<TelephonyConnectionListener> getTelephonyConnectionListeners() {
+ return new ArrayList<>(mTelephonyListeners);
+ }
}
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 766a75e..55456f6 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -65,6 +65,7 @@
import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneConnection;
+import com.android.phone.FrameworksUtils;
import com.android.phone.MMIDialogActivity;
import com.android.phone.PhoneUtils;
import com.android.phone.R;
@@ -498,7 +499,8 @@
IntentFilter intentFilter = new IntentFilter(
TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
- registerReceiver(mTtyBroadcastReceiver, intentFilter);
+ registerReceiver(mTtyBroadcastReceiver, intentFilter,
+ android.Manifest.permission.MODIFY_PHONE_STATE, null);
}
@Override
@@ -2459,7 +2461,7 @@
if (showDialog) {
Log.d(this, "Creating UT Data enable dialog");
String message = SuppServicesUiUtil.makeMessage(context, suppKey, phone);
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ AlertDialog.Builder builder = FrameworksUtils.makeAlertDialogBuilder(context);
DialogInterface.OnClickListener networkSettingsClickListener =
new Dialog.OnClickListener() {
@Override
diff --git a/src/com/android/services/telephony/rcs/RcsFeatureController.java b/src/com/android/services/telephony/rcs/RcsFeatureController.java
index 7834903..cc1a2cc 100644
--- a/src/com/android/services/telephony/rcs/RcsFeatureController.java
+++ b/src/com/android/services/telephony/rcs/RcsFeatureController.java
@@ -33,6 +33,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.imsphone.ImsRegistrationCallbackHelper;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.phone.ImsStateCallbackController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -139,6 +140,8 @@
// ImsService is gone.
updateConnectionStatus(manager);
setupConnectionToService(manager);
+ ImsStateCallbackController.getInstance()
+ .notifyExternalRcsStateChanged(mSlotId, true, true);
} catch (ImsException e) {
updateConnectionStatus(null /*manager*/);
// Use deprecated Exception for compatibility.
diff --git a/src/com/android/services/telephony/rcs/TelephonyRcsService.java b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
index 034382c..dfcea74 100644
--- a/src/com/android/services/telephony/rcs/TelephonyRcsService.java
+++ b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
@@ -32,7 +32,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.PhoneConfigurationManager;
+import com.android.internal.telephony.metrics.RcsStats;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.phone.ImsStateCallbackController;
import com.android.phone.R;
import java.io.FileDescriptor;
@@ -163,6 +165,7 @@
mFeatureControllers = new SparseArray<>(numSlots);
mSlotToAssociatedSubIds = new SparseArray<>(numSlots);
mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext);
+ RcsStats.getInstance().registerUceCallback();
}
@VisibleForTesting
@@ -173,6 +176,7 @@
mSlotToAssociatedSubIds = new SparseArray<>(numSlots);
sResourceProxy = resourceProxy;
mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext);
+ RcsStats.getInstance().registerUceCallback();
}
/**
@@ -310,6 +314,9 @@
}
// Only start the connection procedure if we have active features.
if (c.hasActiveFeatures()) c.connect();
+
+ ImsStateCallbackController.getInstance()
+ .notifyExternalRcsStateChanged(slotId, false, c.hasActiveFeatures());
}
/**
diff --git a/tests/src/com/android/phone/ImsStateCallbackControllerTest.java b/tests/src/com/android/phone/ImsStateCallbackControllerTest.java
new file mode 100644
index 0000000..75690bb
--- /dev/null
+++ b/tests/src/com/android/phone/ImsStateCallbackControllerTest.java
@@ -0,0 +1,895 @@
+/*
+ * Copyright (C) 2021 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;
+
+import static android.telephony.ims.ImsStateCallback.REASON_IMS_SERVICE_DISCONNECTED;
+import static android.telephony.ims.ImsStateCallback.REASON_IMS_SERVICE_NOT_READY;
+import static android.telephony.ims.ImsStateCallback.REASON_NO_IMS_SERVICE_CONFIGURED;
+import static android.telephony.ims.ImsStateCallback.REASON_SUBSCRIPTION_INACTIVE;
+import static android.telephony.ims.feature.ImsFeature.FEATURE_MMTEL;
+import static android.telephony.ims.feature.ImsFeature.FEATURE_RCS;
+
+import static com.android.ims.FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED;
+import static com.android.ims.FeatureConnector.UNAVAILABLE_REASON_IMS_UNSUPPORTED;
+import static com.android.ims.FeatureConnector.UNAVAILABLE_REASON_NOT_READY;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyRegistryManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableLooper;
+import android.util.Log;
+
+import com.android.ims.FeatureConnector;
+import com.android.ims.ImsManager;
+import com.android.ims.RcsFeatureManager;
+import com.android.internal.telephony.IImsStateCallback;
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.ims.ImsResolver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.lang.reflect.Field;
+import java.util.concurrent.Executor;
+
+/**
+ * Unit tests for RcsProvisioningMonitor
+ */
+public class ImsStateCallbackControllerTest {
+ private static final String TAG = "ImsStateCallbackControllerTest";
+ private static final int FAKE_SUB_ID_BASE = 0x0FFFFFF0;
+
+ private static final int SLOT_0 = 0;
+ private static final int SLOT_1 = 1;
+
+ private static final int SLOT_0_SUB_ID = 1;
+ private static final int SLOT_1_SUB_ID = 2;
+ private static final int SLOT_2_SUB_ID = 3;
+
+ private ImsStateCallbackController mImsStateCallbackController;
+ private Handler mHandler;
+ private HandlerThread mHandlerThread;
+ private TestableLooper mLooper;
+ @Mock private SubscriptionManager mSubscriptionManager;
+ private SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener;
+ @Mock private TelephonyRegistryManager mTelephonyRegistryManager;
+ @Mock private ITelephony.Stub mITelephony;
+ @Mock private RcsFeatureManager mRcsFeatureManager;
+ @Mock private ImsManager mMmTelFeatureManager;
+ @Mock private ImsStateCallbackController.MmTelFeatureConnectorFactory mMmTelFeatureFactory;
+ @Mock private ImsStateCallbackController.RcsFeatureConnectorFactory mRcsFeatureFactory;
+ @Mock private FeatureConnector<ImsManager> mMmTelFeatureConnectorSlot0;
+ @Mock private FeatureConnector<ImsManager> mMmTelFeatureConnectorSlot1;
+ @Mock private FeatureConnector<RcsFeatureManager> mRcsFeatureConnectorSlot0;
+ @Mock private FeatureConnector<RcsFeatureManager> mRcsFeatureConnectorSlot1;
+ @Captor ArgumentCaptor<FeatureConnector.Listener<ImsManager>> mMmTelConnectorListenerSlot0;
+ @Captor ArgumentCaptor<FeatureConnector.Listener<ImsManager>> mMmTelConnectorListenerSlot1;
+ @Captor ArgumentCaptor<FeatureConnector.Listener<RcsFeatureManager>> mRcsConnectorListenerSlot0;
+ @Captor ArgumentCaptor<FeatureConnector.Listener<RcsFeatureManager>> mRcsConnectorListenerSlot1;
+ @Mock private PhoneGlobals mPhone;
+ @Mock ImsStateCallbackController.PhoneFactoryProxy mPhoneFactoryProxy;
+ @Mock Phone mPhoneSlot0;
+ @Mock Phone mPhoneSlot1;
+ @Mock private IBinder mBinder0;
+ @Mock private IBinder mBinder1;
+ @Mock private IBinder mBinder2;
+ @Mock private IBinder mBinder3;
+ @Mock private IImsStateCallback mCallback0;
+ @Mock private IImsStateCallback mCallback1;
+ @Mock private IImsStateCallback mCallback2;
+ @Mock private IImsStateCallback mCallback3;
+ @Mock private ImsResolver mImsResolver;
+
+ private Executor mExecutor = new Executor() {
+ @Override
+ public void execute(Runnable r) {
+ r.run();
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ when(mPhone.getMainExecutor()).thenReturn(mExecutor);
+ when(mPhone.getSystemServiceName(eq(SubscriptionManager.class)))
+ .thenReturn(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ when(mPhone.getSystemService(eq(Context.TELEPHONY_SUBSCRIPTION_SERVICE)))
+ .thenReturn(mSubscriptionManager);
+ when(mPhone.getSystemServiceName(eq(TelephonyRegistryManager.class)))
+ .thenReturn(Context.TELEPHONY_REGISTRY_SERVICE);
+ when(mPhone.getSystemService(eq(Context.TELEPHONY_REGISTRY_SERVICE)))
+ .thenReturn(mTelephonyRegistryManager);
+ when(mPhoneFactoryProxy.getPhone(eq(0))).thenReturn(mPhoneSlot0);
+ when(mPhoneFactoryProxy.getPhone(eq(1))).thenReturn(mPhoneSlot1);
+ when(mPhoneSlot0.getSubId()).thenReturn(SLOT_0_SUB_ID);
+ when(mPhoneSlot1.getSubId()).thenReturn(SLOT_1_SUB_ID);
+
+ when(mCallback0.asBinder()).thenReturn(mBinder0);
+ when(mCallback1.asBinder()).thenReturn(mBinder1);
+ when(mCallback2.asBinder()).thenReturn(mBinder2);
+ when(mCallback3.asBinder()).thenReturn(mBinder3);
+
+ // slot 0
+ when(mImsResolver.isImsServiceConfiguredForFeature(eq(0), eq(FEATURE_MMTEL)))
+ .thenReturn(true);
+ when(mImsResolver.isImsServiceConfiguredForFeature(eq(0), eq(FEATURE_RCS)))
+ .thenReturn(true);
+
+ // slot 1
+ when(mImsResolver.isImsServiceConfiguredForFeature(eq(1), eq(FEATURE_MMTEL)))
+ .thenReturn(true);
+ when(mImsResolver.isImsServiceConfiguredForFeature(eq(1), eq(FEATURE_RCS)))
+ .thenReturn(true);
+
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ mSubChangedListener = (SubscriptionManager.OnSubscriptionsChangedListener)
+ invocation.getArguments()[0];
+ return null;
+ }
+ }).when(mTelephonyRegistryManager).addOnSubscriptionsChangedListener(
+ any(SubscriptionManager.OnSubscriptionsChangedListener.class),
+ any());
+
+ mHandlerThread = new HandlerThread("ImsStateCallbackControllerTest");
+ mHandlerThread.start();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mImsStateCallbackController != null) {
+ mImsStateCallbackController.destroy();
+ mImsStateCallbackController = null;
+ }
+
+ if (mLooper != null) {
+ mLooper.destroy();
+ mLooper = null;
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testMmTelRegisterThenUnregisterCallback() throws Exception {
+ createController(1);
+
+ mImsStateCallbackController
+ .registerImsStateCallback(SLOT_0_SUB_ID, FEATURE_MMTEL, mCallback0);
+ processAllMessages();
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback0));
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+
+ mImsStateCallbackController.unregisterImsStateCallback(mCallback0);
+ processAllMessages();
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback0));
+ }
+
+ @Test
+ @SmallTest
+ public void testMmTelConnectionUnavailable() throws Exception {
+ createController(1);
+
+ mImsStateCallbackController
+ .registerImsStateCallback(SLOT_0_SUB_ID, FEATURE_MMTEL, mCallback0);
+ processAllMessages();
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback0));
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+
+ mMmTelConnectorListenerSlot0.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_NOT_READY);
+ processAllMessages();
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
+
+ mMmTelConnectorListenerSlot0.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_IMS_UNSUPPORTED);
+ processAllMessages();
+ verify(mCallback0, times(1)).onUnavailable(REASON_NO_IMS_SERVICE_CONFIGURED);
+
+ mImsStateCallbackController.unregisterImsStateCallback(mCallback0);
+ processAllMessages();
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback0));
+ }
+
+ @Test
+ @SmallTest
+ public void testMmTelConnectionReady() throws Exception {
+ createController(1);
+
+ mImsStateCallbackController
+ .registerImsStateCallback(SLOT_0_SUB_ID, FEATURE_MMTEL, mCallback0);
+ processAllMessages();
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback0));
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+ verify(mCallback0, times(0)).onAvailable();
+
+ mMmTelConnectorListenerSlot0.getValue().connectionReady(null);
+ processAllMessages();
+ verify(mCallback0, atLeastOnce()).onAvailable();
+
+ mImsStateCallbackController.unregisterImsStateCallback(mCallback0);
+ processAllMessages();
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback0));
+ }
+
+ @Test
+ @SmallTest
+ public void testMmTelIgnoreDuplicatedConsecutiveReason() throws Exception {
+ createController(1);
+
+ mImsStateCallbackController
+ .registerImsStateCallback(SLOT_0_SUB_ID, FEATURE_MMTEL, mCallback0);
+ processAllMessages();
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback0));
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+
+ mMmTelConnectorListenerSlot0.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_IMS_UNSUPPORTED);
+ processAllMessages();
+ verify(mCallback0, times(1)).onUnavailable(REASON_NO_IMS_SERVICE_CONFIGURED);
+
+ mMmTelConnectorListenerSlot0.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_IMS_UNSUPPORTED);
+ processAllMessages();
+ verify(mCallback0, times(1)).onUnavailable(REASON_NO_IMS_SERVICE_CONFIGURED);
+
+ mImsStateCallbackController.unregisterImsStateCallback(mCallback0);
+ processAllMessages();
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback0));
+ }
+
+ @Test
+ @SmallTest
+ public void testRcsRegisterThenUnregisterCallback() throws Exception {
+ createController(1);
+
+ mImsStateCallbackController
+ .registerImsStateCallback(SLOT_0_SUB_ID, FEATURE_RCS, mCallback0);
+ processAllMessages();
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback0));
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+
+ mImsStateCallbackController.unregisterImsStateCallback(mCallback0);
+ processAllMessages();
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback0));
+ }
+
+ @Test
+ @SmallTest
+ public void testRcsConnectionUnavailable() throws Exception {
+ createController(1);
+
+ mImsStateCallbackController
+ .registerImsStateCallback(SLOT_0_SUB_ID, FEATURE_RCS, mCallback0);
+ processAllMessages();
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback0));
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+
+ // TelephonyRcsService notifying active features
+ mImsStateCallbackController.notifyExternalRcsStateChanged(SLOT_0, false, true);
+ processAllMessages();
+
+ mRcsConnectorListenerSlot0.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_NOT_READY);
+ processAllMessages();
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
+
+ mRcsConnectorListenerSlot0.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_IMS_UNSUPPORTED);
+ processAllMessages();
+ verify(mCallback0, times(1)).onUnavailable(REASON_NO_IMS_SERVICE_CONFIGURED);
+
+ mImsStateCallbackController.unregisterImsStateCallback(mCallback0);
+ processAllMessages();
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback0));
+ }
+
+ @Test
+ @SmallTest
+ public void testRcsConnectionReady() throws Exception {
+ createController(1);
+
+ mImsStateCallbackController
+ .registerImsStateCallback(SLOT_0_SUB_ID, FEATURE_RCS, mCallback0);
+ processAllMessages();
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback0));
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+
+ // TelephonyRcsService notifying active features
+ mImsStateCallbackController.notifyExternalRcsStateChanged(SLOT_0, false, true);
+ processAllMessages();
+
+ mRcsConnectorListenerSlot0.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_NOT_READY);
+ processAllMessages();
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
+
+ mRcsConnectorListenerSlot0.getValue().connectionReady(null);
+ processAllMessages();
+ verify(mCallback0, times(0)).onAvailable();
+
+ // RcsFeatureController notifying STATE_READY
+ mImsStateCallbackController.notifyExternalRcsStateChanged(SLOT_0, true, true);
+ processAllMessages();
+ verify(mCallback0, times(1)).onAvailable();
+
+ mRcsConnectorListenerSlot0.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_DISCONNECTED);
+ processAllMessages();
+ verify(mCallback0, times(2)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+
+ mRcsConnectorListenerSlot0.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_NOT_READY);
+ processAllMessages();
+ verify(mCallback0, times(2)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
+
+ // RcsFeatureController notifying STATE_READY
+ mImsStateCallbackController.notifyExternalRcsStateChanged(SLOT_0, true, true);
+ processAllMessages();
+ verify(mCallback0, times(1)).onAvailable();
+
+ mRcsConnectorListenerSlot0.getValue().connectionReady(null);
+ processAllMessages();
+ verify(mCallback0, times(2)).onAvailable();
+
+ mImsStateCallbackController.unregisterImsStateCallback(mCallback0);
+ processAllMessages();
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback0));
+ }
+
+ @Test
+ @SmallTest
+ public void testRcsHasNoActiveFeature() throws Exception {
+ createController(1);
+
+ mImsStateCallbackController
+ .registerImsStateCallback(SLOT_0_SUB_ID, FEATURE_RCS, mCallback0);
+ processAllMessages();
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback0));
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+
+ // TelephonyRcsService notifying NO active feature
+ mImsStateCallbackController.notifyExternalRcsStateChanged(SLOT_0, false, false);
+ processAllMessages();
+ verify(mCallback0, times(1)).onUnavailable(REASON_NO_IMS_SERVICE_CONFIGURED);
+
+ mRcsConnectorListenerSlot0.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_NOT_READY);
+ processAllMessages();
+ verify(mCallback0, times(0)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
+
+ mRcsConnectorListenerSlot0.getValue().connectionReady(null);
+ processAllMessages();
+ verify(mCallback0, times(0)).onAvailable();
+
+ mImsStateCallbackController.unregisterImsStateCallback(mCallback0);
+ processAllMessages();
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback0));
+ }
+
+ @Test
+ @SmallTest
+ public void testRcsIgnoreDuplicatedConsecutiveReason() throws Exception {
+ createController(1);
+
+ mImsStateCallbackController
+ .registerImsStateCallback(SLOT_0_SUB_ID, FEATURE_RCS, mCallback0);
+ processAllMessages();
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback0));
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+
+ // TelephonyRcsService notifying active features
+ mImsStateCallbackController.notifyExternalRcsStateChanged(SLOT_0, false, true);
+ processAllMessages();
+
+ mRcsConnectorListenerSlot0.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_NOT_READY);
+ processAllMessages();
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
+
+ mRcsConnectorListenerSlot0.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_NOT_READY);
+ processAllMessages();
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
+
+ mImsStateCallbackController.unregisterImsStateCallback(mCallback0);
+ processAllMessages();
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback0));
+ }
+
+ @Test
+ @SmallTest
+ public void testCallbackRemovedWhenSubInfoChanged() throws Exception {
+ createController(2);
+
+ mImsStateCallbackController
+ .registerImsStateCallback(SLOT_0_SUB_ID, FEATURE_MMTEL, mCallback0);
+ mImsStateCallbackController
+ .registerImsStateCallback(SLOT_1_SUB_ID, FEATURE_RCS, mCallback1);
+ processAllMessages();
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback0));
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback1));
+
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+ verify(mCallback1, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+
+ makeFakeActiveSubIds(0);
+ mExecutor.execute(() -> mSubChangedListener.onSubscriptionsChanged());
+ processAllMessages();
+
+ verify(mCallback0, times(1)).onUnavailable(REASON_SUBSCRIPTION_INACTIVE);
+ verify(mCallback1, times(1)).onUnavailable(REASON_SUBSCRIPTION_INACTIVE);
+
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback0));
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback1));
+ }
+
+ @Test
+ @SmallTest
+ public void testCarrierConfigurationChanged() throws Exception {
+ createController(2);
+
+ when(mImsResolver.isImsServiceConfiguredForFeature(eq(1), eq(FEATURE_MMTEL)))
+ .thenReturn(false);
+
+ mImsStateCallbackController
+ .registerImsStateCallback(SLOT_0_SUB_ID, FEATURE_MMTEL, mCallback0);
+ mImsStateCallbackController
+ .registerImsStateCallback(SLOT_1_SUB_ID, FEATURE_MMTEL, mCallback1);
+ mImsStateCallbackController
+ .registerImsStateCallback(SLOT_1_SUB_ID, FEATURE_RCS, mCallback2);
+ processAllMessages();
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback0));
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback1));
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback2));
+
+ // check initial reason
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+ verify(mCallback1, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+ verify(mCallback2, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+
+ verify(mCallback0, times(0)).onUnavailable(REASON_NO_IMS_SERVICE_CONFIGURED);
+ verify(mCallback1, times(0)).onUnavailable(REASON_NO_IMS_SERVICE_CONFIGURED);
+ verify(mCallback2, times(0)).onUnavailable(REASON_NO_IMS_SERVICE_CONFIGURED);
+
+ // ensure only one reason reported until now
+ verify(mCallback0, times(1)).onUnavailable(anyInt());
+ verify(mCallback1, times(1)).onUnavailable(anyInt());
+ verify(mCallback2, times(1)).onUnavailable(anyInt());
+
+ // state change in RCS for slot 0
+ mRcsConnectorListenerSlot0.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_NOT_READY);
+
+ // ensure there is no change, since callbacks are not interested RCS on slot 0
+ verify(mCallback0, times(1)).onUnavailable(anyInt());
+ verify(mCallback1, times(1)).onUnavailable(anyInt());
+ verify(mCallback2, times(1)).onUnavailable(anyInt());
+
+ // carrier config changed, no MMTEL package for slot 1
+ mImsStateCallbackController.notifyCarrierConfigChanged(SLOT_1);
+ processAllMessages();
+
+ // only the callback for MMTEL of slot 1 received the reason
+ verify(mCallback0, times(0)).onUnavailable(REASON_NO_IMS_SERVICE_CONFIGURED);
+ verify(mCallback1, times(1)).onUnavailable(REASON_NO_IMS_SERVICE_CONFIGURED);
+ verify(mCallback2, times(0)).onUnavailable(REASON_NO_IMS_SERVICE_CONFIGURED);
+
+ // ensure no other callbacks
+ verify(mCallback0, times(1)).onUnavailable(anyInt());
+ verify(mCallback1, times(2)).onUnavailable(anyInt());
+ verify(mCallback2, times(1)).onUnavailable(anyInt());
+
+ mMmTelConnectorListenerSlot1.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_NOT_READY);
+ mMmTelConnectorListenerSlot1.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_DISCONNECTED);
+
+ // resons except REASON_NO_IMS_SERVICE_CONFIGURED are discared
+ verify(mCallback0, times(1)).onUnavailable(anyInt());
+ verify(mCallback1, times(2)).onUnavailable(anyInt());
+ verify(mCallback2, times(1)).onUnavailable(anyInt());
+
+ // IMS package for MMTEL of slot 1 is added
+ when(mImsResolver.isImsServiceConfiguredForFeature(eq(1), eq(FEATURE_MMTEL)))
+ .thenReturn(true);
+ mImsStateCallbackController.notifyCarrierConfigChanged(SLOT_1);
+ processAllMessages();
+
+ // ensure the callback to MMTEL of slot 1 only received REASON_IMS_SERVICE_DISCONNECTED
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+ verify(mCallback1, times(2)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+ verify(mCallback2, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+
+ // ensure no other reason repored
+ verify(mCallback0, times(1)).onUnavailable(anyInt());
+ verify(mCallback1, times(3)).onUnavailable(anyInt());
+ verify(mCallback2, times(1)).onUnavailable(anyInt());
+
+ // carrier config changed, no MMTEL package for slot 1
+ when(mImsResolver.isImsServiceConfiguredForFeature(eq(1), eq(FEATURE_MMTEL)))
+ .thenReturn(false);
+ mImsStateCallbackController.notifyCarrierConfigChanged(SLOT_1);
+ mImsStateCallbackController.notifyCarrierConfigChanged(SLOT_1);
+ processAllMessages();
+ // only the callback for MMTEL of slot 1 received the reason
+ verify(mCallback0, times(0)).onUnavailable(REASON_NO_IMS_SERVICE_CONFIGURED);
+ verify(mCallback1, times(2)).onUnavailable(REASON_NO_IMS_SERVICE_CONFIGURED);
+ verify(mCallback2, times(0)).onUnavailable(REASON_NO_IMS_SERVICE_CONFIGURED);
+
+ // ensure no other reason repored
+ verify(mCallback0, times(1)).onUnavailable(anyInt());
+ verify(mCallback1, times(4)).onUnavailable(anyInt());
+ verify(mCallback2, times(1)).onUnavailable(anyInt());
+
+ mMmTelConnectorListenerSlot1.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_NOT_READY);
+
+ // resons except REASON_NO_IMS_SERVICE_CONFIGURED are discared
+ verify(mCallback0, times(1)).onUnavailable(anyInt());
+ verify(mCallback1, times(4)).onUnavailable(anyInt());
+ verify(mCallback2, times(1)).onUnavailable(anyInt());
+
+ // IMS package for MMTEL of slot 1 is added
+ when(mImsResolver.isImsServiceConfiguredForFeature(eq(1), eq(FEATURE_MMTEL)))
+ .thenReturn(true);
+ mImsStateCallbackController.notifyCarrierConfigChanged(SLOT_1);
+ processAllMessages();
+
+ // ensure the callback to MMTEL of slot 1
+ // there is a pending reason UNAVAILABLE_REASON_NOT_READY
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+ verify(mCallback1, times(2)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+ verify(mCallback1, times(1)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
+ verify(mCallback2, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+
+ // ensure no other reason repored
+ verify(mCallback0, times(1)).onUnavailable(anyInt());
+ verify(mCallback1, times(5)).onUnavailable(anyInt());
+ verify(mCallback2, times(1)).onUnavailable(anyInt());
+
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback0));
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback1));
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback2));
+
+ mImsStateCallbackController.unregisterImsStateCallback(mCallback0);
+ mImsStateCallbackController.unregisterImsStateCallback(mCallback1);
+ mImsStateCallbackController.unregisterImsStateCallback(mCallback2);
+ processAllMessages();
+
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback0));
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback1));
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback2));
+ }
+
+ @Test
+ @SmallTest
+ public void testMultiSubscriptions() throws Exception {
+ createController(2);
+
+ // registration
+ mImsStateCallbackController
+ .registerImsStateCallback(SLOT_0_SUB_ID, FEATURE_MMTEL, mCallback0);
+ mImsStateCallbackController
+ .registerImsStateCallback(SLOT_0_SUB_ID, FEATURE_RCS, mCallback1);
+ mImsStateCallbackController
+ .registerImsStateCallback(SLOT_1_SUB_ID, FEATURE_MMTEL, mCallback2);
+ mImsStateCallbackController
+ .registerImsStateCallback(SLOT_1_SUB_ID, FEATURE_RCS, mCallback3);
+ processAllMessages();
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback0));
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback1));
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback2));
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback3));
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+ verify(mCallback1, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+ verify(mCallback2, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+ verify(mCallback3, times(1)).onUnavailable(REASON_IMS_SERVICE_DISCONNECTED);
+
+ // TelephonyRcsService notifying active features
+ // slot 0
+ mImsStateCallbackController.notifyExternalRcsStateChanged(SLOT_0, false, true);
+ // slot 1
+ mImsStateCallbackController.notifyExternalRcsStateChanged(SLOT_1, false, true);
+ processAllMessages();
+
+ verify(mCallback0, times(1)).onUnavailable(anyInt());
+ verify(mCallback1, times(1)).onUnavailable(anyInt());
+ verify(mCallback2, times(1)).onUnavailable(anyInt());
+ verify(mCallback3, times(1)).onUnavailable(anyInt());
+
+ verify(mCallback0, times(0)).onAvailable();
+ verify(mCallback1, times(0)).onAvailable();
+ verify(mCallback2, times(0)).onAvailable();
+ verify(mCallback3, times(0)).onAvailable();
+
+ // connectionUnavailable
+ mMmTelConnectorListenerSlot0.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_NOT_READY);
+ processAllMessages();
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
+ verify(mCallback0, times(2)).onUnavailable(anyInt());
+ verify(mCallback1, times(1)).onUnavailable(anyInt());
+ verify(mCallback1, times(1)).onUnavailable(anyInt());
+ verify(mCallback2, times(1)).onUnavailable(anyInt());
+ verify(mCallback2, times(1)).onUnavailable(anyInt());
+ verify(mCallback3, times(1)).onUnavailable(anyInt());
+ verify(mCallback3, times(1)).onUnavailable(anyInt());
+
+ mRcsConnectorListenerSlot0.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_NOT_READY);
+ processAllMessages();
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
+ verify(mCallback0, times(2)).onUnavailable(anyInt());
+ verify(mCallback1, times(1)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
+ verify(mCallback1, times(2)).onUnavailable(anyInt());
+ verify(mCallback2, times(1)).onUnavailable(anyInt());
+ verify(mCallback2, times(1)).onUnavailable(anyInt());
+ verify(mCallback3, times(1)).onUnavailable(anyInt());
+ verify(mCallback3, times(1)).onUnavailable(anyInt());
+
+ mMmTelConnectorListenerSlot1.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_NOT_READY);
+ processAllMessages();
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
+ verify(mCallback0, times(2)).onUnavailable(anyInt());
+ verify(mCallback1, times(1)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
+ verify(mCallback1, times(2)).onUnavailable(anyInt());
+ verify(mCallback2, times(1)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
+ verify(mCallback2, times(2)).onUnavailable(anyInt());
+ verify(mCallback3, times(1)).onUnavailable(anyInt());
+ verify(mCallback3, times(1)).onUnavailable(anyInt());
+
+ mRcsConnectorListenerSlot1.getValue()
+ .connectionUnavailable(UNAVAILABLE_REASON_NOT_READY);
+ processAllMessages();
+ verify(mCallback0, times(1)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
+ verify(mCallback0, times(2)).onUnavailable(anyInt());
+ verify(mCallback1, times(1)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
+ verify(mCallback1, times(2)).onUnavailable(anyInt());
+ verify(mCallback2, times(1)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
+ verify(mCallback2, times(2)).onUnavailable(anyInt());
+ verify(mCallback3, times(1)).onUnavailable(REASON_IMS_SERVICE_NOT_READY);
+ verify(mCallback3, times(2)).onUnavailable(anyInt());
+
+ // connectionReady
+ mMmTelConnectorListenerSlot0.getValue().connectionReady(null);
+ processAllMessages();
+ verify(mCallback0, times(1)).onAvailable();
+ verify(mCallback1, times(0)).onAvailable();
+ verify(mCallback2, times(0)).onAvailable();
+ verify(mCallback3, times(0)).onAvailable();
+ verify(mCallback0, times(2)).onUnavailable(anyInt());
+ verify(mCallback1, times(2)).onUnavailable(anyInt());
+ verify(mCallback2, times(2)).onUnavailable(anyInt());
+ verify(mCallback3, times(2)).onUnavailable(anyInt());
+
+ mRcsConnectorListenerSlot0.getValue().connectionReady(null);
+ processAllMessages();
+ verify(mCallback0, times(1)).onAvailable();
+ verify(mCallback1, times(0)).onAvailable();
+ verify(mCallback2, times(0)).onAvailable();
+ verify(mCallback3, times(0)).onAvailable();
+ verify(mCallback0, times(2)).onUnavailable(anyInt());
+ verify(mCallback1, times(2)).onUnavailable(anyInt());
+ verify(mCallback2, times(2)).onUnavailable(anyInt());
+ verify(mCallback3, times(2)).onUnavailable(anyInt());
+
+ mImsStateCallbackController.notifyExternalRcsStateChanged(SLOT_0, true, true);
+ processAllMessages();
+ verify(mCallback0, times(1)).onAvailable();
+ verify(mCallback1, times(1)).onAvailable();
+ verify(mCallback2, times(0)).onAvailable();
+ verify(mCallback3, times(0)).onAvailable();
+ verify(mCallback0, times(2)).onUnavailable(anyInt());
+ verify(mCallback1, times(2)).onUnavailable(anyInt());
+ verify(mCallback2, times(2)).onUnavailable(anyInt());
+ verify(mCallback3, times(2)).onUnavailable(anyInt());
+
+ mMmTelConnectorListenerSlot1.getValue().connectionReady(null);
+ processAllMessages();
+ verify(mCallback0, times(1)).onAvailable();
+ verify(mCallback1, times(1)).onAvailable();
+ verify(mCallback2, times(1)).onAvailable();
+ verify(mCallback3, times(0)).onAvailable();
+ verify(mCallback0, times(2)).onUnavailable(anyInt());
+ verify(mCallback1, times(2)).onUnavailable(anyInt());
+ verify(mCallback2, times(2)).onUnavailable(anyInt());
+ verify(mCallback3, times(2)).onUnavailable(anyInt());
+
+ mRcsConnectorListenerSlot1.getValue().connectionReady(null);
+ processAllMessages();
+ verify(mCallback0, times(1)).onAvailable();
+ verify(mCallback1, times(1)).onAvailable();
+ verify(mCallback2, times(1)).onAvailable();
+ verify(mCallback3, times(0)).onAvailable();
+ verify(mCallback0, times(2)).onUnavailable(anyInt());
+ verify(mCallback1, times(2)).onUnavailable(anyInt());
+ verify(mCallback2, times(2)).onUnavailable(anyInt());
+ verify(mCallback3, times(2)).onUnavailable(anyInt());
+
+ mImsStateCallbackController.notifyExternalRcsStateChanged(SLOT_1, true, true);
+ processAllMessages();
+ verify(mCallback0, times(1)).onAvailable();
+ verify(mCallback1, times(1)).onAvailable();
+ verify(mCallback2, times(1)).onAvailable();
+ verify(mCallback3, times(1)).onAvailable();
+ verify(mCallback0, times(2)).onUnavailable(anyInt());
+ verify(mCallback1, times(2)).onUnavailable(anyInt());
+ verify(mCallback2, times(2)).onUnavailable(anyInt());
+ verify(mCallback3, times(2)).onUnavailable(anyInt());
+
+ // unregistration
+ mImsStateCallbackController.unregisterImsStateCallback(mCallback0);
+ processAllMessages();
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback0));
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback1));
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback2));
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback3));
+
+ mImsStateCallbackController.unregisterImsStateCallback(mCallback1);
+ processAllMessages();
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback0));
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback1));
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback2));
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback3));
+
+ mImsStateCallbackController.unregisterImsStateCallback(mCallback2);
+ processAllMessages();
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback0));
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback1));
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback2));
+ assertTrue(mImsStateCallbackController.isRegistered(mCallback3));
+
+ mImsStateCallbackController.unregisterImsStateCallback(mCallback3);
+ processAllMessages();
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback0));
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback1));
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback2));
+ assertFalse(mImsStateCallbackController.isRegistered(mCallback3));
+ }
+
+ @Test
+ @SmallTest
+ public void testSlotUpdates() throws Exception {
+ createController(1);
+
+ verify(mMmTelFeatureConnectorSlot0, times(1)).connect();
+ verify(mRcsFeatureConnectorSlot0, times(1)).connect();
+ verify(mMmTelFeatureConnectorSlot0, times(0)).disconnect();
+ verify(mRcsFeatureConnectorSlot0, times(0)).disconnect();
+
+ // Add a new slot.
+ mImsStateCallbackController.updateFeatureControllerSize(2);
+
+ // connect in slot 1
+ verify(mMmTelFeatureConnectorSlot1, times(1)).connect();
+ verify(mRcsFeatureConnectorSlot1, times(1)).connect();
+
+ // no change in slot 0
+ verify(mMmTelFeatureConnectorSlot0, times(1)).connect();
+ verify(mRcsFeatureConnectorSlot0, times(1)).connect();
+
+ // Remove a slot.
+ mImsStateCallbackController.updateFeatureControllerSize(1);
+
+ // destroy in slot 1
+ verify(mMmTelFeatureConnectorSlot1, times(1)).disconnect();
+ verify(mRcsFeatureConnectorSlot1, times(1)).disconnect();
+
+ // no change in slot 0
+ verify(mMmTelFeatureConnectorSlot0, times(0)).disconnect();
+ verify(mRcsFeatureConnectorSlot0, times(0)).disconnect();
+ }
+
+ private void createController(int slotCount) throws Exception {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ makeFakeActiveSubIds(slotCount);
+
+ when(mMmTelFeatureFactory
+ .create(any(), eq(0), any(), mMmTelConnectorListenerSlot0.capture(), any()))
+ .thenReturn(mMmTelFeatureConnectorSlot0);
+ when(mMmTelFeatureFactory
+ .create(any(), eq(1), any(), mMmTelConnectorListenerSlot1.capture(), any()))
+ .thenReturn(mMmTelFeatureConnectorSlot1);
+ when(mRcsFeatureFactory
+ .create(any(), eq(0), mRcsConnectorListenerSlot0.capture(), any(), any()))
+ .thenReturn(mRcsFeatureConnectorSlot0);
+ when(mRcsFeatureFactory
+ .create(any(), eq(1), mRcsConnectorListenerSlot1.capture(), any(), any()))
+ .thenReturn(mRcsFeatureConnectorSlot1);
+
+ mImsStateCallbackController =
+ new ImsStateCallbackController(mPhone, mHandlerThread.getLooper(),
+ slotCount, mMmTelFeatureFactory, mRcsFeatureFactory, mImsResolver);
+
+ replaceInstance(ImsStateCallbackController.class,
+ "mPhoneFactoryProxy", mImsStateCallbackController, mPhoneFactoryProxy);
+ mImsStateCallbackController.onSubChanged();
+
+ mHandler = mImsStateCallbackController.getHandler();
+ try {
+ mLooper = new TestableLooper(mHandler.getLooper());
+ } catch (Exception e) {
+ logd("Unable to create looper from handler.");
+ }
+
+ verify(mRcsFeatureConnectorSlot0, atLeastOnce()).connect();
+ verify(mMmTelFeatureConnectorSlot0, atLeastOnce()).connect();
+
+ if (slotCount == 1) {
+ verify(mRcsFeatureConnectorSlot1, times(0)).connect();
+ verify(mMmTelFeatureConnectorSlot1, times(0)).connect();
+ } else {
+ verify(mRcsFeatureConnectorSlot1, atLeastOnce()).connect();
+ verify(mMmTelFeatureConnectorSlot1, atLeastOnce()).connect();
+ }
+ }
+
+ private static void replaceInstance(final Class c,
+ final String instanceName, final Object obj, final Object newValue) throws Exception {
+ Field field = c.getDeclaredField(instanceName);
+ field.setAccessible(true);
+ field.set(obj, newValue);
+ }
+
+ private void makeFakeActiveSubIds(int count) {
+ final int[] subIds = new int[count];
+ for (int i = 0; i < count; i++) {
+ subIds[i] = FAKE_SUB_ID_BASE + i;
+ }
+ when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(subIds);
+ }
+
+ private void processAllMessages() {
+ while (!mLooper.getLooper().getQueue().isIdle()) {
+ mLooper.processAllMessages();
+ }
+ }
+
+ private static void logd(String str) {
+ Log.d(TAG, str);
+ }
+}
diff --git a/tests/src/com/android/phone/RcsProvisioningMonitorTest.java b/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
index 4fba922..8873402 100644
--- a/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
+++ b/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
@@ -16,6 +16,10 @@
package com.android.phone;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS__EVENT__DMA_CHANGED;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS__EVENT__TRIGGER_RCS_RECONFIGURATION;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
@@ -64,6 +68,7 @@
import com.android.ims.FeatureConnector;
import com.android.ims.RcsFeatureManager;
import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.metrics.RcsStats;
import org.junit.After;
import org.junit.Before;
@@ -179,6 +184,10 @@
private IRcsConfigCallback mCallback;
@Mock
private PackageManager mPackageManager;
+ @Mock
+ private RcsStats mRcsStats;
+ @Mock
+ private RcsStats.RcsProvisioningCallback mRcsProvisioningCallback;
private Executor mExecutor = new Executor() {
@Override
@@ -305,7 +314,6 @@
@Test
@SmallTest
public void testInitWithSavedConfig() throws Exception {
- ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
createMonitor(3);
for (int i = 0; i < 3; i++) {
@@ -313,10 +321,6 @@
mRcsProvisioningMonitor.getConfig(FAKE_SUB_ID_BASE + i)));
}
- verify(mPhone, times(3)).sendBroadcast(captorIntent.capture(), any());
- Intent capturedIntent = captorIntent.getAllValues().get(1);
- assertEquals(ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE,
- capturedIntent.getAction());
verify(mIImsConfig, times(3)).notifyRcsAutoConfigurationReceived(any(), anyBoolean());
}
@@ -324,14 +328,8 @@
@SmallTest
public void testInitWithoutSavedConfig() throws Exception {
when(mCursor.getBlob(anyInt())).thenReturn(null);
- ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
createMonitor(3);
- verify(mPhone, times(3)).sendBroadcast(captorIntent.capture(), any());
- Intent capturedIntent = captorIntent.getAllValues().get(1);
-
- assertEquals(ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE,
- capturedIntent.getAction());
//Should not notify null config
verify(mIImsConfig, never()).notifyRcsAutoConfigurationReceived(any(), anyBoolean());
}
@@ -340,16 +338,12 @@
@SmallTest
public void testSubInfoChanged() throws Exception {
createMonitor(3);
- ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
for (int i = 0; i < 3; i++) {
assertTrue(Arrays.equals(CONFIG_DEFAULT.getBytes(),
mRcsProvisioningMonitor.getConfig(FAKE_SUB_ID_BASE + i)));
}
- verify(mPhone, times(3)).sendBroadcast(captorIntent.capture(), any());
- Intent capturedIntent = captorIntent.getAllValues().get(1);
- assertEquals(ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE,
- capturedIntent.getAction());
+
verify(mIImsConfig, times(3)).notifyRcsAutoConfigurationReceived(any(), anyBoolean());
makeFakeActiveSubIds(1);
@@ -401,14 +395,20 @@
@SmallTest
public void testCarrierConfigChanged() throws Exception {
createMonitor(1);
+ // should not broadcast message if carrier config is not ready
+ verify(mPhone, never()).sendBroadcast(any(), any());
+
when(mPackageManager.hasSystemFeature(
eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(true);
ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
+ mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
mBundle.putBoolean(
CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, true);
+
broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
processAllMessages();
- verify(mPhone, atLeastOnce()).sendBroadcast(captorIntent.capture(), any());
+
+ verify(mPhone, times(1)).sendBroadcast(captorIntent.capture(), any());
Intent capturedIntent = captorIntent.getValue();
assertEquals(capturedIntent.getAction(),
ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE);
@@ -421,7 +421,8 @@
CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
processAllMessages();
- verify(mPhone, atLeastOnce()).sendBroadcast(captorIntent.capture(), any());
+
+ verify(mPhone, times(2)).sendBroadcast(captorIntent.capture(), any());
capturedIntent = captorIntent.getValue();
assertEquals(capturedIntent.getAction(),
ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE);
@@ -435,7 +436,8 @@
eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(false);
broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
processAllMessages();
- verify(mPhone, atLeastOnce()).sendBroadcast(captorIntent.capture(), any());
+
+ verify(mPhone, times(3)).sendBroadcast(captorIntent.capture(), any());
capturedIntent = captorIntent.getValue();
assertEquals(capturedIntent.getAction(),
ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE);
@@ -479,6 +481,7 @@
when(mPackageManager.hasSystemFeature(
eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(false);
+ mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
mBundle.putBoolean(
CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
@@ -580,13 +583,39 @@
@Test
@SmallTest
public void testSendBroadcastWhenDmaChanged() throws Exception {
- createMonitor(3);
- verify(mPhone, times(3)).sendBroadcast(any(), any());
-
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(null);
+ mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+ createMonitor(1);
updateDefaultMessageApplication(DEFAULT_MESSAGING_APP2);
processAllMessages();
- verify(mPhone, times(6)).sendBroadcast(any(), any());
+ // should not broadcast message as no carrier config change happens
+ verify(mPhone, never()).sendBroadcast(any(), any());
+
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mBundle);
+ when(mPackageManager.hasSystemFeature(
+ eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(true);
+ ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
+ mBundle.putBoolean(
+ CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, true);
+
+ broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
+ processAllMessages();
+
+ verify(mPhone, times(1)).sendBroadcast(captorIntent.capture(), any());
+ Intent capturedIntent = captorIntent.getValue();
+ assertEquals(capturedIntent.getAction(),
+ ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE);
+
+ updateDefaultMessageApplication(DEFAULT_MESSAGING_APP1);
+ processAllMessages();
+
+ // should broadcast message when default messaging application changed if carrier config
+ // has been loaded
+ verify(mPhone, times(2)).sendBroadcast(captorIntent.capture(), any());
+ capturedIntent = captorIntent.getValue();
+ assertEquals(capturedIntent.getAction(),
+ ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE);
}
@Test
@@ -611,6 +640,7 @@
when(mPackageManager.hasSystemFeature(
eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(true);
+ mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
mBundle.putBoolean(
CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, true);
broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
@@ -694,6 +724,7 @@
when(mPackageManager.hasSystemFeature(
eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(true);
+ mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
mBundle.putBoolean(
CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, true);
broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
@@ -746,6 +777,66 @@
assertNull(mRcsProvisioningMonitor.getImsFeatureValidationOverride(FAKE_SUB_ID_BASE));
}
+ @Test
+ @SmallTest
+ public void testMetricsAcsNotUsed() throws Exception {
+ createMonitor(1);
+
+ // Not used ACS
+ mBundle.putBoolean(CarrierConfigManager.KEY_USE_ACS_FOR_RCS_BOOL, false);
+ broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
+ processAllMessages();
+ mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE, CONFIG_DEFAULT.getBytes(), false);
+ processAllMessages();
+ verify(mRcsStats, never()).onRcsAcsProvisioningStats(anyInt(), anyInt(),
+ anyInt(), anyBoolean());
+ }
+
+ @Test
+ @SmallTest
+ public void testMetricsAcsUsed() throws Exception {
+ when(mRcsStats.getRcsProvisioningCallback(anyInt(), anyBoolean()))
+ .thenReturn(mRcsProvisioningCallback);
+ createMonitor(1);
+
+ verify(mIImsConfig, times(1))
+ .notifyRcsAutoConfigurationReceived(any(), anyBoolean());
+ // verify RcsStats.getRcsProvisioningCallback() is called
+ verify(mRcsStats, times(1)).getRcsProvisioningCallback(
+ eq(FAKE_SUB_ID_BASE), anyBoolean());
+ // verify registered callback obj which comes from RcsStats.getRcsProvisioningCallback()
+ verify(mIImsConfig, times(1))
+ .addRcsConfigCallback(eq(mRcsProvisioningCallback));
+
+ // Config data received and ACS used
+ int errorCode = 200;
+ mBundle.putBoolean(CarrierConfigManager.KEY_USE_ACS_FOR_RCS_BOOL, true);
+ broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
+ processAllMessages();
+ mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE, CONFIG_DEFAULT.getBytes(), false);
+ processAllMessages();
+ verify(mRcsStats, times(1)).onRcsAcsProvisioningStats(eq(FAKE_SUB_ID_BASE), eq(errorCode),
+ eq(RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML), anyBoolean());
+ }
+
+ @Test
+ @SmallTest
+ public void testMetricsClientProvisioningStats() throws Exception {
+ createMonitor(1);
+
+ // reconfig trigger
+ mRcsProvisioningMonitor.requestReconfig(FAKE_SUB_ID_BASE);
+ processAllMessages();
+ verify(mRcsStats, times(1)).onRcsClientProvisioningStats(eq(FAKE_SUB_ID_BASE),
+ eq(RCS_CLIENT_PROVISIONING_STATS__EVENT__TRIGGER_RCS_RECONFIGURATION));
+
+ // DMA changed
+ updateDefaultMessageApplication(DEFAULT_MESSAGING_APP2);
+ processAllMessages();
+ verify(mRcsStats, times(1)).onRcsClientProvisioningStats(eq(FAKE_SUB_ID_BASE),
+ eq(RCS_CLIENT_PROVISIONING_STATS__EVENT__DMA_CHANGED));
+ }
+
private void createMonitor(int subCount) throws Exception {
if (Looper.myLooper() == null) {
Looper.prepare();
@@ -755,7 +846,7 @@
.thenReturn(mFeatureConnector);
when(mFeatureManager.getConfig()).thenReturn(mIImsConfig);
mRcsProvisioningMonitor = new RcsProvisioningMonitor(mPhone, mHandlerThread.getLooper(),
- mRoleManager, mFeatureFactory);
+ mRoleManager, mFeatureFactory, mRcsStats);
mHandler = mRcsProvisioningMonitor.getHandler();
try {
mLooper = new TestableLooper(mHandler.getLooper());
diff --git a/tests/src/com/android/phone/SimPhonebookProviderTest.java b/tests/src/com/android/phone/SimPhonebookProviderTest.java
index 848ba42..d8518f8 100644
--- a/tests/src/com/android/phone/SimPhonebookProviderTest.java
+++ b/tests/src/com/android/phone/SimPhonebookProviderTest.java
@@ -181,16 +181,16 @@
public void query_entityFiles_multiSim_returnsCursorWithRowForEachSimEf() {
setupSimsWithSubscriptionIds(2, 3, 7);
- mIccPhoneBook.setRecordsSize(2, IccConstants.EF_ADN, 10, 25);
- mIccPhoneBook.setRecordsSize(2, IccConstants.EF_FDN, 5, 20);
- mIccPhoneBook.setRecordsSize(2, IccConstants.EF_SDN, 15, 20);
- mIccPhoneBook.setRecordsSize(3, IccConstants.EF_ADN, 100, 30);
+ mIccPhoneBook.setupEfWithSizes(2, IccConstants.EF_ADN, 10, 25);
+ mIccPhoneBook.setupEfWithSizes(2, IccConstants.EF_FDN, 5, 20);
+ mIccPhoneBook.setupEfWithSizes(2, IccConstants.EF_SDN, 15, 20);
+ mIccPhoneBook.setupEfWithSizes(3, IccConstants.EF_ADN, 100, 30);
// These Will be omitted from results because zero size indicates the EF is not supported.
- mIccPhoneBook.setRecordsSize(3, IccConstants.EF_FDN, 0, 0);
- mIccPhoneBook.setRecordsSize(3, IccConstants.EF_SDN, 0, 0);
- mIccPhoneBook.setRecordsSize(7, IccConstants.EF_ADN, 0, 0);
- mIccPhoneBook.setRecordsSize(7, IccConstants.EF_FDN, 0, 0);
- mIccPhoneBook.setRecordsSize(7, IccConstants.EF_SDN, 0, 0);
+ mIccPhoneBook.setupEfWithSizes(3, IccConstants.EF_FDN, 0, 0);
+ mIccPhoneBook.setupEfWithSizes(3, IccConstants.EF_SDN, 0, 0);
+ mIccPhoneBook.setupEfWithSizes(7, IccConstants.EF_ADN, 0, 0);
+ mIccPhoneBook.setupEfWithSizes(7, IccConstants.EF_FDN, 0, 0);
+ mIccPhoneBook.setupEfWithSizes(7, IccConstants.EF_SDN, 0, 0);
String[] projection = {
ElementaryFiles.SLOT_INDEX, ElementaryFiles.SUBSCRIPTION_ID,
@@ -212,15 +212,44 @@
public void query_entityFiles_simWithZeroSizes_returnsEmptyCursor() {
setupSimsWithSubscriptionIds(1);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 0, 0);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_FDN, 0, 0);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_SDN, 0, 0);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 0, 0);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_FDN, 0, 0);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_SDN, 0, 0);
try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, null, null, null)) {
assertThat(cursor).hasCount(0);
}
}
+ /**
+ * USIM cards support more than 255 records by having multiple files for one EF type but
+ * IIccPhoneBook.getAdnRecordsSizeForSubscriber returns the size for a single file and so is
+ * inaccurate for such SIMs.
+ *
+ * <p>See b/201385523#comment4 and b/201685690
+ */
+ @Test
+ public void query_entityFiles_adnRecordCountExceedsSize_returnsAdnRecordCountAsMaxRecords() {
+ setupSimsWithSubscriptionIds(1);
+
+ // There are 400 records returned by getAdnRecordsInEfForSubscriber but the count returned
+ // by getAdnRecordsSizeForSubscriber is only 200.
+ AdnRecord[] records = mIccPhoneBook.createEmptyRecords(IccConstants.EF_ADN, 400);
+ mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 200, 20);
+ mIccPhoneBook.setRecords(1, IccConstants.EF_ADN, records);
+
+ String[] projection = {
+ ElementaryFiles.SUBSCRIPTION_ID, ElementaryFiles.EF_TYPE,
+ ElementaryFiles.MAX_RECORDS
+ };
+ try (Cursor cursor = mResolver.query(
+ ElementaryFiles.CONTENT_URI, projection, null, null)) {
+ assertThat(cursor).hasCount(1);
+ assertThat(cursor)
+ .atRow(0).hasRowValues(1, ElementaryFiles.EF_ADN, 400);
+ }
+ }
+
@Test
public void query_entityFilesItem_nullProjection_returnsCursorWithCorrectProjection() {
setupSimsWithSubscriptionIds(1);
@@ -549,7 +578,7 @@
@Test
public void query_adnRecords_zeroSizeEf_throwsCorrectException() {
setupSimsWithSubscriptionIds(1);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 0, 0);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 0, 0);
IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
() -> mResolver.query(SimRecords.getContentUri(1, EF_ADN), null, null, null));
@@ -618,9 +647,9 @@
@Test
public void query_itemUriEmptyRecord_returnsEmptyCursor() {
setupSimsWithSubscriptionIds(1);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 30);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_FDN, 1, 30);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_SDN, 1, 30);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 30);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_FDN, 1, 30);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_SDN, 1, 30);
try (Cursor adnItem = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1),
null, null, null);
@@ -638,9 +667,9 @@
@Test
public void query_itemUriIndexExceedsMax_returnsEmptyCursor() {
setupSimsWithSubscriptionIds(1);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 30);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_FDN, 1, 30);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_SDN, 1, 30);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 30);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_FDN, 1, 30);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_SDN, 1, 30);
try (Cursor adnItem = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 2),
null, null, null);
@@ -741,7 +770,7 @@
@Test
public void insert_efFull_throwsCorrectException() {
setupSimsWithSubscriptionIds(1);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 30);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 30);
mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Existing", "8005550101");
ContentValues values = new ContentValues();
@@ -843,7 +872,7 @@
@Test
public void insert_phoneNumberOmitted_throwsCorrectException() {
setupSimsWithSubscriptionIds(1);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 25);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 25);
ContentValues values = new ContentValues();
values.put(SimRecords.NAME, "Name");
@@ -856,7 +885,7 @@
@Test
public void insert_nameTooLong_throwsCorrectException() {
setupSimsWithSubscriptionIds(1);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 25);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 25);
ContentValues values = new ContentValues();
// Name is limited to 11 characters when the max record size is 25
@@ -879,7 +908,7 @@
@Test
public void insert_phoneNumberTooLong_throwsCorrectException() {
setupSimsWithSubscriptionIds(1);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 25);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 25);
ContentValues values = new ContentValues();
values.put(SimRecords.NAME, "Name");
@@ -895,7 +924,7 @@
@Test
public void insert_numberWithInvalidCharacters_throwsCorrectException() {
setupSimsWithSubscriptionIds(1);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 32);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 32);
ContentValues values = new ContentValues();
values.put(SimRecords.NAME, "Name");
@@ -915,7 +944,7 @@
@Test
public void insert_unsupportedColumn_throwsCorrectException() {
setupSimsWithSubscriptionIds(1);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 25);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 25);
ContentValues values = new ContentValues();
values.put(SimRecords.NAME, "Name");
@@ -1007,7 +1036,7 @@
@Test
public void update_indexExceedingMax_returnsZero() {
setupSimsWithSubscriptionIds(1);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 30);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 30);
ContentValues values = new ContentValues();
values.put(SimRecords.NAME, "name");
@@ -1046,7 +1075,7 @@
public void delete_indexExceedingMax_returnsZero() {
setupSimsWithSubscriptionIds(1);
mIccPhoneBook.makeAllEfsSupported(1);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 30);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 30);
int result = mResolver.delete(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 2), null);
@@ -1067,7 +1096,7 @@
@Test
public void update_nameOrNumberTooLong_throwsCorrectException() {
setupSimsWithSubscriptionIds(1);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 25);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 25);
mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Initial", "8005550101");
ContentValues values = new ContentValues();
@@ -1097,7 +1126,7 @@
@Test
public void update_numberWithInvalidCharacters_throwsCorrectException() {
setupSimsWithSubscriptionIds(1);
- mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 32);
+ mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 32);
mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Initial", "8005550101");
ContentValues values = new ContentValues();
@@ -1324,7 +1353,7 @@
// The key for both maps is the (subscription ID, efid)
private Map<Pair<Integer, Integer>, AdnRecord[]> mRecords = new HashMap<>();
// The value is the single record size
- private Map<Pair<Integer, Integer>, Integer> mRecordSizes = new HashMap<>();
+ private Map<Pair<Integer, Integer>, int[]> mRecordSizes = new HashMap<>();
private int mDefaultSubscriptionId = 101;
@@ -1332,7 +1361,7 @@
// Assume that if records are being added then the test wants it to be a valid
// elementary file so set sizes as well.
if (!mRecordSizes.containsKey(key)) {
- setRecordsSize(key.first, key.second,
+ setupEfWithSizes(key.first, key.second,
Math.max(record.getRecId(), DEFAULT_RECORDS_COUNT), DEFAULT_RECORD_SIZE);
}
mRecords.get(key)[record.getRecId() - 1] = record;
@@ -1402,18 +1431,33 @@
* subscription IDs.
*/
public void makeAllEfsSupported(int subscriptionId) {
- setRecordsSize(subscriptionId, IccConstants.EF_ADN, DEFAULT_RECORDS_COUNT,
+ setupEfWithSizes(subscriptionId, IccConstants.EF_ADN, DEFAULT_RECORDS_COUNT,
DEFAULT_RECORD_SIZE);
- setRecordsSize(subscriptionId, IccConstants.EF_FDN, DEFAULT_RECORDS_COUNT,
+ setupEfWithSizes(subscriptionId, IccConstants.EF_FDN, DEFAULT_RECORDS_COUNT,
DEFAULT_RECORD_SIZE);
- setRecordsSize(subscriptionId, IccConstants.EF_SDN, DEFAULT_RECORDS_COUNT,
+ setupEfWithSizes(subscriptionId, IccConstants.EF_SDN, DEFAULT_RECORDS_COUNT,
DEFAULT_RECORD_SIZE);
}
+ public void setRecords(int subscriptionId, int efid, AdnRecord[] records) {
+ mRecords.put(Pair.create(subscriptionId, efid), records);
+ }
+
public void setRecordsSize(int subscriptionId, int efid, int maxRecordCount,
int maxRecordSize) {
+ setRecordsSize(Pair.create(subscriptionId, efid), maxRecordCount, maxRecordSize);
+ }
+
+ private void setRecordsSize(Pair<Integer, Integer> key, int maxRecordCount,
+ int maxRecordSize) {
+ int[] sizes = { maxRecordSize, maxRecordSize * maxRecordCount, maxRecordCount };
+ mRecordSizes.put(key, sizes);
+ }
+
+ public void setupEfWithSizes(int subscriptionId, int efid, int maxRecordCount,
+ int maxRecordSize) {
Pair<Integer, Integer> key = Pair.create(subscriptionId, efid);
- mRecordSizes.put(key, maxRecordSize);
+ setRecordsSize(key, maxRecordCount, maxRecordSize);
AdnRecord[] records = mRecords.computeIfAbsent(key, unused ->
createEmptyRecords(efid, maxRecordCount));
if (records.length < maxRecordCount) {
@@ -1421,7 +1465,7 @@
}
}
- private AdnRecord[] createEmptyRecords(int efid, int count) {
+ AdnRecord[] createEmptyRecords(int efid, int count) {
AdnRecord[] records = new AdnRecord[count];
for (int i = 0; i < records.length; i++) {
if (records[i] == null) {
@@ -1499,12 +1543,11 @@
@Override
public int[] getAdnRecordsSizeForSubscriber(int subId, int efid) {
Pair<Integer, Integer> key = Pair.create(subId, efid);
- Integer recordSize = mRecordSizes.get(key);
- if (recordSize == null) {
+ int[] recordsSize = mRecordSizes.get(key);
+ if (recordsSize == null) {
return new int[]{0, 0, 0};
}
- int count = mRecords.get(key).length;
- return new int[]{recordSize, recordSize * count, count};
+ return recordsSize;
}
@Override
diff --git a/tests/src/com/android/services/telephony/ImsConferenceTest.java b/tests/src/com/android/services/telephony/ImsConferenceTest.java
index 3bc5ee8..9d2f5ac 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceTest.java
@@ -622,6 +622,46 @@
}
/**
+ * Tests a scenario where a handover connection arrives via
+ * {@link TelephonyConnection#onOriginalConnectionRedialed(
+ * com.android.internal.telephony.Connection)}. During this process, the conference properties
+ * get updated. Since the original connection is null at this point, we need to verify that
+ * the remotely hosted property is retained from before the original connection was nulled.
+ */
+ @Test
+ public void testIsConferenceRemotelyHostedCachingOnSRVCC() {
+ mConferenceHost.setIsImsConnection(true);
+ when(mConferenceHost.getMockImsPhoneConnection().isMultiparty()).thenReturn(true);
+ when(mConferenceHost.getMockImsPhoneConnection().isConferenceHost()).thenReturn(true);
+
+ // Start out with a valid conference host.
+ ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
+ mMockTelephonyConnectionServiceProxy, mConferenceHost,
+ null /* phoneAccountHandle */, () -> false /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder().build());
+
+ // By default it is not remotely hosted.
+ assertFalse(imsConference.isRemotelyHosted());
+ assertEquals(0,
+ imsConference.getConnectionProperties() & Connection.PROPERTY_REMOTELY_HOSTED);
+
+ // Simulate a change to the original connection due to srvcc
+ com.android.internal.telephony.Connection previousOriginalConnection =
+ mConferenceHost.getMockImsPhoneConnection();
+ mConferenceHost.setMockImsPhoneConnection(null);
+
+ // Trigger the property update which takes place when the original connection changes.
+ mConferenceHost.getTelephonyConnectionListeners().forEach(
+ l -> l.onConnectionPropertiesChanged(mConferenceHost,
+ mConferenceHost.getConnectionProperties()));
+
+ // Should still NOT be remotely hosted based on cached value.
+ assertFalse(imsConference.isRemotelyHosted());
+ assertEquals(0,
+ imsConference.getConnectionProperties() & Connection.PROPERTY_REMOTELY_HOSTED);
+ }
+
+ /**
* Verifies that an ImsConference can handle SIP and TEL URIs for both the P-Associated-Uri and
* conference event package identities.
*/
diff --git a/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java b/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
index cfdc2fd..b7fe988 100644
--- a/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
@@ -23,9 +23,11 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.net.Uri;
import android.os.Looper;
import android.telecom.Conference;
import android.telecom.Connection;
+import android.telecom.PhoneAccount;
import android.test.suitebuilder.annotation.SmallTest;
import org.junit.Before;
@@ -35,7 +37,9 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
/**
* Tests the functionality in TelephonyConferenceController.java
@@ -111,6 +115,46 @@
}
/**
+ * Verify the connection with "Conference Call" with PROPERTY_IS_DOWNGRADED_CONFERENCE
+ * during SRVCC
+ */
+ @Test
+ @SmallTest
+ public void testSrvccConferenceConnection() {
+ when(mTestTelephonyConnectionA.mMockRadioConnection.getCall()
+ .isMultiparty()).thenReturn(true);
+ when(mTestTelephonyConnectionB.mMockRadioConnection.getCall()
+ .isMultiparty()).thenReturn(true);
+
+ List<Connection> listConnections = Arrays.asList(
+ mTestTelephonyConnectionA, mTestTelephonyConnectionB);
+ when(mMockTelephonyConnectionServiceProxy.getAllConnections()).thenReturn(listConnections);
+
+ mTestTelephonyConnectionA.setAddress(
+ Uri.fromParts(PhoneAccount.SCHEME_TEL, "Conference Call", null), 0);
+ mTestTelephonyConnectionA.setTelephonyConnectionProperties(
+ Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE);
+ mTestTelephonyConnectionB.setAddress(
+ Uri.fromParts(PhoneAccount.SCHEME_TEL, "5551213", null), 0);
+ mTestTelephonyConnectionB.setTelephonyConnectionProperties(
+ Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE);
+
+ // add telephony connection B
+ mControllerTest.add(mTestTelephonyConnectionB);
+
+ // add telephony connection A
+ mControllerTest.add(mTestTelephonyConnectionA);
+
+ // verify the connection with "Conference Call" has PROPERTY_IS_DOWNGRADED_CONFERENCE
+ assertTrue((mTestTelephonyConnectionA.getConnectionProperties()
+ & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0);
+
+ // verify the connection with "5551213" hasn't PROPERTY_IS_DOWNGRADED_CONFERENCE
+ assertFalse((mTestTelephonyConnectionB.getConnectionProperties()
+ & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0);
+ }
+
+ /**
* Behavior: add telephony connection B and A to conference controller,
* set status for connections and merged calls, remove one call
* Assumption: after performing the behaviours, the status of Connection A is STATE_ACTIVE;
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index 817e9a7..e149d3b 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -305,4 +305,12 @@
public PersistableBundle getCarrierConfigBundle() {
return mCarrierConfig;
}
+
+ public ImsPhoneConnection getMockImsPhoneConnection() {
+ return mImsPhoneConnection;
+ }
+
+ public void setMockImsPhoneConnection(ImsPhoneConnection connection) {
+ mImsPhoneConnection = connection;
+ }
}