Merge "Map answered elsewhere telephony disconnect cause to telecom equivalent." into nyc-mr1-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 0a9a6a5..26b0c7a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -58,7 +58,16 @@
<protected-broadcast android:name= "android.intent.action.CARRIER_SIGNAL_REDIRECTED" />
<protected-broadcast android:name= "android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED" />
<protected-broadcast android:name= "android.intent.action.CARRIER_SIGNAL_PCO_VALUE" />
+ <protected-broadcast android:name= "android.intent.action.VOICEMAIL_SMS_RECEIVED" />
<protected-broadcast android:name= "com.android.intent.isim_refresh" />
+ <protected-broadcast android:name= "com.android.ims.IMS_SERVICE_UP" />
+ <protected-broadcast android:name= "com.android.ims.IMS_SERVICE_DOWN" />
+ <protected-broadcast android:name= "com.android.ims.IMS_INCOMING_CALL" />
+ <protected-broadcast android:name= "com.android.ims.internal.uce.UCE_SERVICE_UP" />
+ <protected-broadcast android:name= "com.android.ims.internal.uce.UCE_SERVICE_DOWN" />
+ <protected-broadcast android:name= "com.android.imsconnection.DISCONNECTED" />
+ <protected-broadcast android:name= "com.android.intent.action.IMS_FEATURE_CHANGED" />
+ <protected-broadcast android:name= "com.android.intent.action.IMS_CONFIG_CHANGED" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.CALL_PHONE" />
@@ -328,7 +337,7 @@
non-voice-capable tablets and regular phone devices. -->
<activity android:name="MobileNetworkSettings"
android:label="@string/settings_label"
- android:theme="@style/SettingsLight">
+ android:theme="@style/NetworkOperatorsSettingsTheme">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />
@@ -655,23 +664,26 @@
</intent-filter>
</provider>
<receiver android:name="com.android.phone.vvm.omtp.sms.OmtpMessageReceiver"
- android:exported="true">
+ android:exported="true"
+ androidprv:systemUserOnly="true">
<intent-filter>
<action android:name="android.intent.action.VOICEMAIL_SMS_RECEIVED"/>
</intent-filter>
</receiver>
<receiver
android:name="com.android.phone.vvm.omtp.SimChangeReceiver"
- android:exported="true">
+ android:exported="true"
+ androidprv:systemUserOnly="true">
<intent-filter>
<action android:name="android.telephony.action.CARRIER_CONFIG_CHANGED" />
<action android:name="android.intent.action.SIM_STATE_CHANGED" />
</intent-filter>
</receiver>
<receiver
- android:name="com.android.phone.vvm.omtp.OmtpBootCompletedReceiver"
+ android:name="com.android.phone.vvm.omtp.VvmBootCompletedReceiver"
android:exported="true"
- android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
+ android:permission="android.permission.RECEIVE_BOOT_COMPLETED"
+ androidprv:systemUserOnly="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
@@ -679,7 +691,8 @@
<receiver
android:name="com.android.phone.vvm.omtp.fetch.FetchVoicemailReceiver"
android:exported="true"
- android:permission="com.android.voicemail.permission.READ_VOICEMAIL">
+ android:permission="com.android.voicemail.permission.READ_VOICEMAIL"
+ androidprv:systemUserOnly="true">
<intent-filter>
<action android:name="android.intent.action.FETCH_VOICEMAIL" />
<data
@@ -691,14 +704,16 @@
<receiver
android:name="com.android.phone.vvm.omtp.sync.OmtpVvmSyncReceiver"
android:exported="true"
- android:permission="com.android.voicemail.permission.READ_VOICEMAIL">
+ android:permission="com.android.voicemail.permission.READ_VOICEMAIL"
+ androidprv:systemUserOnly="true">
<intent-filter>
<action android:name="android.provider.action.SYNC_VOICEMAIL"/>
</intent-filter>
</receiver>
<receiver
android:name="com.android.phone.vvm.omtp.sync.VoicemailProviderChangeReceiver"
- android:exported="true">
+ android:exported="true"
+ androidprv:systemUserOnly="true">
<intent-filter>
<action android:name="android.intent.action.PROVIDER_CHANGED" />
<data
@@ -715,7 +730,8 @@
android:name="com.android.phone.vvm.omtp.sms.OmtpProvisioningService"
android:exported="false" />
- <receiver android:name="com.android.phone.vvm.omtp.VvmPackageInstallReceiver">
+ <receiver android:name="com.android.phone.vvm.omtp.VvmPackageInstallReceiver"
+ androidprv:systemUserOnly="true">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_INSTALL" />
<action android:name="android.intent.action.PACKAGE_ADDED" />
diff --git a/proguard.flags b/proguard.flags
index 41e26a1..e8646eb 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -3,4 +3,8 @@
-keepclassmembers class * {
@**.VisibleForTesting *;
}
+-keep @**.NeededForTesting class *
+-keepclassmembers class * {
+@**.NeededForTesting *;
+}
-verbose
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
index d3106d4..5973fca 100644
--- a/res/values-bn-rBD/strings.xml
+++ b/res/values-bn-rBD/strings.xml
@@ -151,7 +151,7 @@
<string name="vm_change_pin_new_pin" msgid="5412922262839438097">"নতুন পিন"</string>
<string name="vm_change_pin_progress_message" msgid="6727847908454506025">"পিন পরিবর্তন করুন"</string>
<string name="vm_change_pin_error_too_short" msgid="5974971097302710497">"নতুন পিনটি খুবই ছোট৷"</string>
- <string name="vm_change_pin_error_too_long" msgid="8476870806115051865">"নতুন পিনটি খুবই দীর্ঘ৷"</string>
+ <string name="vm_change_pin_error_too_long" msgid="8476870806115051865">"নতুন পিনটি খুবই বড়৷"</string>
<string name="vm_change_pin_error_too_weak" msgid="7883744811891784882">"নতুন পিনটি খুবই দুর্বল৷ একটি শক্তিশালী পাসওয়ার্ডে ধারাবাহিক ক্রম বা পুনরাবৃত্ত সংখ্যা থাকা উচিৎ নয়৷"</string>
<string name="vm_change_pin_error_mismatch" msgid="2754685537970757317">"পুরানো পিন মিলছে না৷"</string>
<string name="vm_change_pin_error_invalid" msgid="3972205462701668653">"নতুন পিনে অবৈধ অক্ষর রয়েছে৷"</string>
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml
index df93f33..e784ddc 100644
--- a/res/values-eu-rES/strings.xml
+++ b/res/values-eu-rES/strings.xml
@@ -152,9 +152,9 @@
<string name="vm_change_pin_progress_message" msgid="6727847908454506025">"PIN kodea aldatzen"</string>
<string name="vm_change_pin_error_too_short" msgid="5974971097302710497">"Laburregia da PIN kode berria."</string>
<string name="vm_change_pin_error_too_long" msgid="8476870806115051865">"Luzeegia da PIN kode berria."</string>
- <string name="vm_change_pin_error_too_weak" msgid="7883744811891784882">"Ahulegia da PIN kode berria. Pasahitz sendo batek ez luke eduki beharko zenbaki-segida edo errepikatutako zenbakirik."</string>
+ <string name="vm_change_pin_error_too_weak" msgid="7883744811891784882">"Ahulegia da PIN kode berria. Pasahitza sendoa izan dadin, ez du izan behar zenbaki-segidarik edo errepikatutako zenbakirik."</string>
<string name="vm_change_pin_error_mismatch" msgid="2754685537970757317">"PIN kode zaharra ez dator bat."</string>
- <string name="vm_change_pin_error_invalid" msgid="3972205462701668653">"Balio ez duten karaktereak ditu PIN kode berriak"</string>
+ <string name="vm_change_pin_error_invalid" msgid="3972205462701668653">"Balio ez duten karaktereak ditu PIN kode berriak."</string>
<string name="vm_change_pin_error_system_error" msgid="6610603326230000207">"Ezin da aldatu PIN kodea"</string>
<string name="mobile_networks" msgid="2843854043339307375">"Sare mugikorraren ezarpenak"</string>
<string name="label_available" msgid="1181658289009300430">"Sare erabilgarriak"</string>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index be7bb8a..41628c9 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -230,11 +230,11 @@
<!-- String.format failed for translation -->
<!-- no translation found for throttle_data_usage_subtext (6029276011123694701) -->
<skip />
- <string name="throttle_data_rate_reduced_subtext" msgid="7492763592720107737">"<xliff:g id="USED_0">%1$s</xliff:g> առավելագույնը գերազանցվել է\nՏվյալների արժեքը նվազել է մինչև <xliff:g id="USED_1">%2$d</xliff:g> ԿԲ/վ"</string>
+ <string name="throttle_data_rate_reduced_subtext" msgid="7492763592720107737">"<xliff:g id="USED_0">%1$s</xliff:g> առավելագույնը գերազանցվել է\nՏվյալների արժեքը նվազել է մինչև <xliff:g id="USED_1">%2$d</xliff:g> կԲ/վ"</string>
<!-- String.format failed for translation -->
<!-- no translation found for throttle_time_frame_subtext (7732763021560399960) -->
<skip />
- <string name="throttle_rate_subtext" msgid="2149102656120726855">"Տվյալների ծավալը կնվազի մինչև <xliff:g id="USED">%1$d</xliff:g> ԿԲ/վ, եթե տվյալների օգտագործման սահմանաչափը գերազանցվի"</string>
+ <string name="throttle_rate_subtext" msgid="2149102656120726855">"Տվյալների ծավալը կնվազի մինչև <xliff:g id="USED">%1$d</xliff:g> կԲ/վ, եթե տվյալների օգտագործման սահմանաչափը գերազանցվի"</string>
<string name="throttle_help_subtext" msgid="3633091498168446044">"Լրացուցիչ տեղեկություններ ձեր օպերատորի բջջային ցանցի տվյալների օգտագործման քաղաքականության մասին"</string>
<string name="cell_broadcast_sms" msgid="5584192824053625842">"Բջջային հեռարձակման SMS"</string>
<string name="enable_disable_cell_bc_sms" msgid="4851147873691392255">"Բջջային հեռարձակման SMS"</string>
@@ -392,7 +392,7 @@
<string name="simContacts_emptyLoading" msgid="2203331234764498011">"Ընթերցում է SIM քարտից..."</string>
<string name="simContacts_empty" msgid="5270660846489561932">"Ձեր SIM քարտում կոնտակտներ չկան:"</string>
<string name="simContacts_title" msgid="1861472842524839921">"Ընտրեք կոնտակտները ներմուծման համար"</string>
- <string name="simContacts_airplaneMode" msgid="5254946758982621072">"Կոնտակտները SIM քարտից ներմուծելու համար անջատեք ինքնաթիռային ռեժիմը:"</string>
+ <string name="simContacts_airplaneMode" msgid="5254946758982621072">"Կոնտակտները SIM քարտից ներմուծելու համար անջատեք Ինքնաթիռի ռեժիմը:"</string>
<string name="enable_pin" msgid="5422767284133234860">"Միացնել/անջատել SIM PIN-ը"</string>
<string name="change_pin" msgid="9174186126330785343">"Փոխել SIM PIN-ը"</string>
<string name="enter_pin_text" msgid="8532615714751931951">"SIM PIN՝"</string>
@@ -443,8 +443,8 @@
<string name="notification_voicemail_no_vm_number" msgid="760963466895609716">"Ձայնային փոստի համարն անհայտ է"</string>
<string name="notification_network_selection_title" msgid="4224455487793492772">"Ծառայություններ չկան"</string>
<string name="notification_network_selection_text" msgid="2607085729661923269">"Ընտրված ցանցը (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) անհասանելի է"</string>
- <string name="incall_error_power_off" msgid="2947938060513306698">"Զանգ կատարելու համար անջատեք ինքնաթիռային ռեժիմը:"</string>
- <string name="incall_error_power_off_wfc" msgid="8711428920632416575">"Զանգ կատարելու համար անջատեք ինքնաթիռային ռեժիմը կամ միացեք անլար ցանցին:"</string>
+ <string name="incall_error_power_off" msgid="2947938060513306698">"Զանգ կատարելու համար անջատեք Ինքնաթիռի ռեժիմը:"</string>
+ <string name="incall_error_power_off_wfc" msgid="8711428920632416575">"Զանգ կատարելու համար անջատեք Ինքնաթիռի ռեժիմը կամ միացեք անլար ցանցին:"</string>
<string name="incall_error_ecm_emergency_only" msgid="738708660612388692">"Սովորական զանգ կատարելու համար դուրս եկեք արտակարգ իրավիճակների հետզանգի ռեժիմից:"</string>
<string name="incall_error_emergency_only" msgid="4678640422710818317">"Ցանցում գրանցված չէ:"</string>
<string name="incall_error_out_of_service" msgid="4100065333878929223">"Բջջային ցանցն անհասանելի է:"</string>
@@ -464,7 +464,7 @@
<string name="emergency_enable_radio_dialog_title" msgid="4627849966634578257">"Արտակարգ իրավիճակների զանգ"</string>
<string name="emergency_enable_radio_dialog_message" msgid="207613549344420291">"Ռադիոն միացվում է..."</string>
<string name="emergency_enable_radio_dialog_retry" msgid="5960061579996526883">"Ծառայությունը մատչելի չէ: Նորից փորձեք…"</string>
- <string name="radio_off_during_emergency_call" msgid="2535800034010306830">"Արտակարգ իրավիճակների զանգի ժամանակ հնարավոր չէ մտնել ինքնաթիռային ռեժիմ:"</string>
+ <string name="radio_off_during_emergency_call" msgid="2535800034010306830">"Արտակարգ իրավիճակների զանգի ժամանակ հնարավոր չէ մտնել ինքնաթիռի ռեժիմ:"</string>
<string name="dial_emergency_error" msgid="1509085166367420355">"Հնարավոր չէ զանգել: <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> համարը արտակարգ իրավիճակի համար չէ:"</string>
<string name="dial_emergency_empty_error" msgid="9130194953830414638">"Հնարավոր չէ զանգել: Հավաքեք արտակարգ իրավիճակի որևէ համար:"</string>
<string name="dialerKeyboardHintText" msgid="9192914825413747792">"Օգտագործեք ստեղնաշարը՝ համարհավաքման համար"</string>
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml
index 57314a4..eb949b9 100644
--- a/res/values-kk-rKZ/strings.xml
+++ b/res/values-kk-rKZ/strings.xml
@@ -149,7 +149,7 @@
<string name="voicemail_default" msgid="2001233554889016880">"Операторыңыз"</string>
<string name="vm_change_pin_old_pin" msgid="7295220109886682573">"Ескі PIN"</string>
<string name="vm_change_pin_new_pin" msgid="5412922262839438097">"Жаңа PIN"</string>
- <string name="vm_change_pin_progress_message" msgid="6727847908454506025">"PIN өзгерту"</string>
+ <string name="vm_change_pin_progress_message" msgid="6727847908454506025">"PIN код өзгертілуде"</string>
<string name="vm_change_pin_error_too_short" msgid="5974971097302710497">"Жаңа PIN код тым қысқа."</string>
<string name="vm_change_pin_error_too_long" msgid="8476870806115051865">"Жаңа PIN код тым ұзын."</string>
<string name="vm_change_pin_error_too_weak" msgid="7883744811891784882">"Жаңа PIN код тым әлсіз. Күшті құпия сөзде үздіксіз реттік немесе қайталанатын таңбалар болмауы тиіс."</string>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
index b03984c..3e20bef 100644
--- a/res/values-ky-rKG/strings.xml
+++ b/res/values-ky-rKG/strings.xml
@@ -152,7 +152,7 @@
<string name="vm_change_pin_progress_message" msgid="6727847908454506025">"PIN код өзгөртүлүүдө"</string>
<string name="vm_change_pin_error_too_short" msgid="5974971097302710497">"Жаңы PIN код өтө эле кыска."</string>
<string name="vm_change_pin_error_too_long" msgid="8476870806115051865">"Жаңы PIN код өтө эле узун."</string>
- <string name="vm_change_pin_error_too_weak" msgid="7883744811891784882">"Жаңы PIN код өтө эле туруксуз. Туруктуу сырсөз үзгүлтүксүз катардан же кайталанган сандардан турбашы керек."</string>
+ <string name="vm_change_pin_error_too_weak" msgid="7883744811891784882">"Жаңы PIN код өтө эле жөнөкөй. Сырсөз күчтүү болушу үчүн анда сандар үзгүлтүксүз катардан турбашы же сандар кайталанбашы керек."</string>
<string name="vm_change_pin_error_mismatch" msgid="2754685537970757317">"Эски PIN код дал келген жок."</string>
<string name="vm_change_pin_error_invalid" msgid="3972205462701668653">"Жаңы PIN коддо жараксыз белгилер бар."</string>
<string name="vm_change_pin_error_system_error" msgid="6610603326230000207">"PIN код өзгөртүлгөн жок"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 8e7498c..6c87001 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -85,7 +85,7 @@
<string name="additional_cdma_call_settings" msgid="8628958775721886909">"Definições adicionais de chamadas CDMA"</string>
<string name="sum_cdma_call_settings" msgid="284753265979035549">"Definições adicionais de chamadas apenas CDMA"</string>
<string name="labelNwService" msgid="4699970172021870983">"Definições do serviço de rede"</string>
- <string name="labelCallerId" msgid="3888899447379069198">"ID do autor da chamada"</string>
+ <string name="labelCallerId" msgid="3888899447379069198">"Identificação de chamadas"</string>
<string name="sum_loading_settings" msgid="1826692909391168620">"A carregar as definições..."</string>
<string name="sum_hide_caller_id" msgid="1071407020290873782">"Ocultar o número em chamadas efectuadas"</string>
<string name="sum_show_caller_id" msgid="6768534125447290401">"Número apresentado em chamadas efectuadas"</string>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 67572ed..870d692 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -54,9 +54,4 @@
<color name="dialer_dialpad_touch_tint">#330288d1</color>
<color name="floating_action_button_touch_tint">#80ffffff</color>
-
- <color name="network_operators_color_primary">#ff263238</color>
- <color name="network_operators_color_primary_dark">#ff21272b</color>
-
- <color name="emergency_dialer_background">#ff263238</color>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 9d2d47f..057352d 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -153,13 +153,6 @@
<item name="android:layout_marginEnd">5dip</item>
</style>
- <!-- Theme for the activity com.android.phone.Settings, which is the
- "Mobile network settings" screen (used on non-voice-capable
- tablets as well as regular phone devices.) -->
- <style name="Theme.Settings" parent="@android:style/Theme.Holo.DialogWhenLarge">
- <item name="android:windowCloseOnTouchOutside">true</item>
- </style>
-
<style name="SettingsLight" parent="android:Theme.Material.Light">
<item name="android:windowBackground">@color/phone_settings_background_color</item>
<item name="android:windowContentOverlay">@null</item>
@@ -186,11 +179,7 @@
<item name="android:textColor">?android:attr/textColorPrimaryInverseDisableOnly</item>
</style>
- <style name="NetworkOperatorsSettingsTheme" parent="@android:style/Theme.Material.Light">
- <item name="android:actionBarTheme">@android:style/ThemeOverlay.Material.Dark.ActionBar</item>
- <item name="android:colorPrimary">@color/network_operators_color_primary</item>
- <item name="android:colorPrimaryDark">@color/network_operators_color_primary_dark</item>
- </style>
+ <style name="NetworkOperatorsSettingsTheme" parent="@android:style/Theme.DeviceDefault.Settings" />
<style name="Empty" parent="@android:style/Theme.Material.Light">
<item name="android:windowIsTranslucent">true</item>
@@ -256,10 +245,9 @@
<item name="android:src">@drawable/overflow_menu</item>
</style>
- <style name="EmergencyDialerTheme" parent="@android:style/Theme.Material.NoActionBar">
- <item name="android:colorPrimary">@color/emergency_dialer_background</item>
- <item name="android:colorPrimaryDark">@color/emergency_dialer_background</item>
- <item name="android:windowBackground">@color/emergency_dialer_background</item>
+ <style name="EmergencyDialerTheme" parent="@*android:style/Theme.DeviceDefault.Settings.Dark.NoActionBar">
+ <item name="android:colorPrimaryDark">?android:attr/colorPrimary</item>
+ <item name="android:windowBackground">?android:attr/colorPrimary</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:homeAsUpIndicator">@drawable/ic_back_arrow</item>
@@ -296,8 +284,4 @@
<item name="android:backgroundDimEnabled">false</item>
</style>
- <style name="Theme.Material.Settings" parent="@android:style/Theme.Material.Settings">
- <item name="@*android:actionBarSize">56dip</item>
- <item name="preferenceBackgroundColor">@drawable/preference_background</item>
- </style>
</resources>
diff --git a/res/xml/vvm_config.xml b/res/xml/vvm_config.xml
index 79edaa6..d55fdb2 100644
--- a/res/xml/vvm_config.xml
+++ b/res/xml/vvm_config.xml
@@ -134,5 +134,6 @@
<string name="vvm_type_string">vvm_type_vvm3</string>
<string name="vvm_client_prefix_string">//VZWVVM</string>
<boolean name="vvm_cellular_data_required_bool" value="true"/>
+ <boolean name="vvm_legacy_mode_enabled_bool" value="true"/>
</pbundle_as_map>
</list>
\ No newline at end of file
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index d74558f..33aba17 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -300,7 +300,8 @@
} else {
prefSet.removePreference(wifiCallingSettings);
}
- } else if (!ImsManager.isWfcEnabledByPlatform(mPhone.getContext())) {
+ } else if (!ImsManager.isWfcEnabledByPlatform(mPhone.getContext()) ||
+ !ImsManager.isWfcProvisionedOnDevice(mPhone.getContext())) {
prefSet.removePreference(wifiCallingSettings);
} else {
int resId = com.android.internal.R.string.wifi_calling_off_summary;
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 3204a9f..5a40322 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -665,9 +665,11 @@
// TODO: Check that the calling packages is privileged for subId specifically.
int privilegeStatus = TelephonyManager.from(mContext).checkCarrierPrivilegesForPackage(
callingPackageName);
+ // Requires the calling app to be either a carrier privileged app or
+ // system privileged app with MODIFY_PHONE_STATE permission.
if (privilegeStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
- throw new SecurityException(
- "Package is not privileged for subId=" + subId + ": " + callingPackageName);
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE,
+ "Require carrier privileges or MODIFY_PHONE_STATE permission.");
}
// This method should block until deleting has completed, so that an error which prevents us
diff --git a/src/com/android/phone/DumpsysHandler.java b/src/com/android/phone/DumpsysHandler.java
new file mode 100644
index 0000000..d2ae38f
--- /dev/null
+++ b/src/com/android/phone/DumpsysHandler.java
@@ -0,0 +1,21 @@
+
+package com.android.phone;
+
+import android.content.Context;
+
+import com.android.phone.vvm.omtp.utils.VvmDumpHandler;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Handles "adb shell dumpsys phone" and bug report dump.
+ */
+public class DumpsysHandler {
+
+ public static void dump(Context context, FileDescriptor fd, PrintWriter writer,
+ String[] args) {
+ // Dump OMTP visual voicemail log.
+ VvmDumpHandler.dump(context, fd, writer, args);
+ }
+}
diff --git a/src/com/android/phone/MobileNetworkSettings.java b/src/com/android/phone/MobileNetworkSettings.java
index 404c976..fd4815e 100644
--- a/src/com/android/phone/MobileNetworkSettings.java
+++ b/src/com/android/phone/MobileNetworkSettings.java
@@ -421,7 +421,6 @@
@Override
protected void onCreate(Bundle icicle) {
if (DBG) log("onCreate:+");
- setTheme(R.style.Theme_Material_Settings);
super.onCreate(icicle);
mHandler = new MyHandler();
diff --git a/src/com/android/phone/NeededForTesting.java b/src/com/android/phone/NeededForTesting.java
new file mode 100644
index 0000000..576598b
--- /dev/null
+++ b/src/com/android/phone/NeededForTesting.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 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 java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.SOURCE)
+public @interface NeededForTesting {
+
+}
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 783878c..808a5d6 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -637,8 +637,53 @@
notifier.updateCallNotifierRegistrationsAfterRadioTechnologyChange();
}
- private void handleAirplaneModeChange(int newMode) {
- if (newMode == AIRPLANE_ON) {
+ private void handleAirplaneModeChange(Context context, int newMode) {
+ int cellState = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.CELL_ON, PhoneConstants.CELL_ON_FLAG);
+ boolean isAirplaneNewlyOn = (newMode == 1);
+ switch (cellState) {
+ case PhoneConstants.CELL_OFF_FLAG:
+ // Airplane mode does not affect the cell radio if user
+ // has turned it off.
+ break;
+ case PhoneConstants.CELL_ON_FLAG:
+ maybeTurnCellOff(context, isAirplaneNewlyOn);
+ break;
+ case PhoneConstants.CELL_OFF_DUE_TO_AIRPLANE_MODE_FLAG:
+ maybeTurnCellOn(context, isAirplaneNewlyOn);
+ break;
+ }
+ }
+
+ /*
+ * Returns true if the radio must be turned off when entering airplane mode.
+ */
+ private boolean isCellOffInAirplaneMode(Context context) {
+ String airplaneModeRadios = Settings.Global.getString(context.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_RADIOS);
+ return airplaneModeRadios == null
+ || airplaneModeRadios.contains(Settings.Global.RADIO_CELL);
+ }
+
+ private void setRadioPowerOff(Context context) {
+ Log.i(LOG_TAG, "Turning radio off - airplane");
+ Settings.Global.putInt(context.getContentResolver(), Settings.Global.CELL_ON,
+ PhoneConstants.CELL_OFF_DUE_TO_AIRPLANE_MODE_FLAG);
+ Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT, 0);
+ PhoneUtils.setRadioPower(false);
+ }
+
+ private void setRadioPowerOn(Context context) {
+ Log.i(LOG_TAG, "Turning radio on - airplane");
+ Settings.Global.putInt(context.getContentResolver(), Settings.Global.CELL_ON,
+ PhoneConstants.CELL_ON_FLAG);
+ Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT,
+ 1);
+ PhoneUtils.setRadioPower(true);
+ }
+
+ private void maybeTurnCellOff(Context context, boolean isAirplaneNewlyOn) {
+ if (isAirplaneNewlyOn) {
// If we are trying to turn off the radio, make sure there are no active
// emergency calls. If there are, switch airplane mode back to off.
if (PhoneUtils.isInEmergencyCall(mCM)) {
@@ -647,13 +692,17 @@
Toast.makeText(this, R.string.radio_off_during_emergency_call, Toast.LENGTH_LONG)
.show();
Log.i(LOG_TAG, "Ignoring airplane mode: emergency call. Turning airplane off");
+ } else if (isCellOffInAirplaneMode(context)) {
+ setRadioPowerOff(context);
} else {
- Log.i(LOG_TAG, "Turning radio off - airplane");
- PhoneUtils.setRadioPower(false);
+ Log.i(LOG_TAG, "Ignoring airplane mode: settings prevent cell radio power off");
}
- } else {
- Log.i(LOG_TAG, "Turning radio on - airplane");
- PhoneUtils.setRadioPower(true);
+ }
+ }
+
+ private void maybeTurnCellOn(Context context, boolean isAirplaneNewlyOn) {
+ if (!isAirplaneNewlyOn) {
+ setRadioPowerOn(context);
}
}
@@ -671,7 +720,7 @@
if (airplaneMode != AIRPLANE_OFF) {
airplaneMode = AIRPLANE_ON;
}
- handleAirplaneModeChange(airplaneMode);
+ handleAirplaneModeChange(context, airplaneMode);
} else if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 2ace8e1..d050576 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -85,6 +85,8 @@
import com.android.internal.util.HexDump;
import com.android.phone.settings.VoicemailNotificationSettingsUtil;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -3289,4 +3291,45 @@
}
}
+ /**
+ * Called when "adb shell dumpsys phone" is invoked. Dump is also automatically invoked when a
+ * bug report is being generated.
+ */
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ if (mPhone.getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ writer.println("Permission Denial: can't dump Phone from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + "without permission "
+ + android.Manifest.permission.DUMP);
+ return;
+ }
+ DumpsysHandler.dump(mPhone.getContext(), fd, writer, args);
+ }
+
+ /**
+ * Get aggregated video call data usage from all subscriptions since boot.
+ * @return total data usage in bytes
+ * {@hide}
+ */
+ @Override
+ public long getVtDataUsage() {
+ mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_NETWORK_USAGE_HISTORY,
+ null);
+
+ // NetworkStatsService keeps tracking the active network interface and identity. It will
+ // record the delta with the corresponding network identity. What we need to do here is
+ // returning total video call data usage from all subscriptions since boot.
+
+ // TODO: Add sub id support in the future. We'll need it when we support DSDA and
+ // simultaneous VT calls.
+ final Phone[] phones = PhoneFactory.getPhones();
+ long total = 0;
+ for (Phone phone : phones) {
+ total += phone.getVtDataUsage();
+ }
+ return total;
+ }
}
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index 4dd7d0b..9f70349 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -64,7 +64,6 @@
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.sip.SipPhone;
import com.android.phone.CallGatewayManager.RawGatewayInfo;
-import com.android.services.telephony.TelephonyConnectionService;
import java.util.Arrays;
import java.util.List;
@@ -2442,7 +2441,7 @@
return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
- static Phone getPhoneForPhoneAccountHandle(PhoneAccountHandle handle) {
+ public static Phone getPhoneForPhoneAccountHandle(PhoneAccountHandle handle) {
if (handle != null && handle.getComponentName().equals(getPstnConnectionServiceName())) {
return getPhoneFromIccId(handle.getId());
}
diff --git a/src/com/android/phone/common/mail/internet/MimeUtility.java b/src/com/android/phone/common/mail/internet/MimeUtility.java
index ba5036f..7402a4c 100644
--- a/src/com/android/phone/common/mail/internet/MimeUtility.java
+++ b/src/com/android/phone/common/mail/internet/MimeUtility.java
@@ -19,7 +19,6 @@
import android.util.Base64;
import android.util.Base64DataException;
import android.util.Base64InputStream;
-import android.util.Log;
import com.android.phone.common.mail.Body;
import com.android.phone.common.mail.BodyPart;
@@ -27,6 +26,7 @@
import com.android.phone.common.mail.MessagingException;
import com.android.phone.common.mail.Multipart;
import com.android.phone.common.mail.Part;
+import com.android.phone.vvm.omtp.VvmLog;
import org.apache.commons.io.IOUtils;
import org.apache.james.mime4j.codec.EncoderUtil;
@@ -267,14 +267,14 @@
* If we are not able to process the body there's nothing we can do about it. Return
* null and let the upper layers handle the missing content.
*/
- Log.e(LOG_TAG, "Unable to getTextFromPart " + oom.toString());
+ VvmLog.e(LOG_TAG, "Unable to getTextFromPart " + oom.toString());
}
catch (Exception e) {
/*
* If we are not able to process the body there's nothing we can do about it. Return
* null and let the upper layers handle the missing content.
*/
- Log.e(LOG_TAG, "Unable to getTextFromPart " + e.toString());
+ VvmLog.e(LOG_TAG, "Unable to getTextFromPart " + e.toString());
}
return null;
}
diff --git a/src/com/android/phone/common/mail/store/ImapConnection.java b/src/com/android/phone/common/mail/store/ImapConnection.java
index de40f2c..0360e3e 100644
--- a/src/com/android/phone/common/mail/store/ImapConnection.java
+++ b/src/com/android/phone/common/mail/store/ImapConnection.java
@@ -15,7 +15,6 @@
*/
package com.android.phone.common.mail.store;
-import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Base64;
@@ -180,18 +179,48 @@
}
} catch (ImapException ie) {
LogUtils.d(TAG, "ImapException", ie);
- final String status = ie.getStatus();
- final String code = ie.getResponseCode();
- final String alertText = ie.getAlertText();
+ String status = ie.getStatus();
+ String statusMessage = ie.getStatusMessage();
+ String alertText = ie.getAlertText();
- // if the response code indicates expired or bad credentials, throw a special exception
- if (ImapConstants.AUTHENTICATIONFAILED.equals(code) ||
- ImapConstants.EXPIRED.equals(code) ||
- (ImapConstants.NO.equals(status) && TextUtils.isEmpty(code))) {
- mImapStore.getImapHelper().handleEvent(OmtpEvents.DATA_BAD_IMAP_CREDENTIAL);
+ if (ImapConstants.NO.equals(status)) {
+ switch (statusMessage) {
+ case ImapConstants.NO_UNKNOWN_USER:
+ mImapStore.getImapHelper().handleEvent(OmtpEvents.DATA_AUTH_UNKNOWN_USER);
+ break;
+ case ImapConstants.NO_UNKNOWN_CLIENT:
+ mImapStore.getImapHelper().handleEvent(OmtpEvents.DATA_AUTH_UNKNOWN_DEVICE);
+ break;
+ case ImapConstants.NO_INVALID_PASSWORD:
+ mImapStore.getImapHelper()
+ .handleEvent(OmtpEvents.DATA_AUTH_INVALID_PASSWORD);
+ break;
+ case ImapConstants.NO_MAILBOX_NOT_INITIALIZED:
+ mImapStore.getImapHelper()
+ .handleEvent(OmtpEvents.DATA_AUTH_MAILBOX_NOT_INITIALIZED);
+ break;
+ case ImapConstants.NO_SERVICE_IS_NOT_PROVISIONED:
+ mImapStore.getImapHelper()
+ .handleEvent(OmtpEvents.DATA_AUTH_SERVICE_NOT_PROVISIONED);
+ break;
+ case ImapConstants.NO_SERVICE_IS_NOT_ACTIVATED:
+ mImapStore.getImapHelper()
+ .handleEvent(OmtpEvents.DATA_AUTH_SERVICE_NOT_ACTIVATED);
+ break;
+ case ImapConstants.NO_USER_IS_BLOCKED:
+ mImapStore.getImapHelper()
+ .handleEvent(OmtpEvents.DATA_AUTH_USER_IS_BLOCKED);
+ break;
+ case ImapConstants.NO_APPLICATION_ERROR:
+ mImapStore.getImapHelper()
+ .handleEvent(OmtpEvents.DATA_REJECTED_SERVER_RESPONSE);
+ default:
+ mImapStore.getImapHelper().handleEvent(OmtpEvents.DATA_BAD_IMAP_CREDENTIAL);
+ }
throw new AuthenticationFailedException(alertText, ie);
}
+ mImapStore.getImapHelper().handleEvent(OmtpEvents.DATA_REJECTED_SERVER_RESPONSE);
throw new MessagingException(alertText, ie);
}
}
@@ -359,16 +388,11 @@
if (!(response.isOk() || response.isContinuationRequest())) {
final String toString = response.toString();
final String status = response.getStatusOrEmpty().getString();
+ final String statusMessage = response.getStatusResponseTextOrEmpty().getString();
final String alert = response.getAlertTextOrEmpty().getString();
final String responseCode = response.getResponseCodeOrEmpty().getString();
destroyResponses();
- mImapStore.getImapHelper().handleEvent(OmtpEvents.DATA_REJECTED_SERVER_RESPONSE);
- // if the response code indicates an error occurred within the server, indicate that
- if (ImapConstants.UNAVAILABLE.equals(responseCode)) {
-
- throw new MessagingException(MessagingException.SERVER_ERROR, alert);
- }
- throw new ImapException(toString, status, alert, responseCode);
+ throw new ImapException(toString, status, statusMessage, alert, responseCode);
}
return responses;
}
diff --git a/src/com/android/phone/common/mail/store/ImapStore.java b/src/com/android/phone/common/mail/store/ImapStore.java
index c8095e5..179d0f2 100644
--- a/src/com/android/phone/common/mail/store/ImapStore.java
+++ b/src/com/android/phone/common/mail/store/ImapStore.java
@@ -130,13 +130,15 @@
private static final long serialVersionUID = 1L;
private final String mStatus;
+ private final String mStatusMessage;
private final String mAlertText;
private final String mResponseCode;
- public ImapException(String message, String status, String alertText,
+ public ImapException(String message, String status, String statusMessage, String alertText,
String responseCode) {
super(message);
mStatus = status;
+ mStatusMessage = statusMessage;
mAlertText = alertText;
mResponseCode = responseCode;
}
@@ -145,6 +147,10 @@
return mStatus;
}
+ public String getStatusMessage() {
+ return mStatusMessage;
+ }
+
public String getAlertText() {
return mAlertText;
}
diff --git a/src/com/android/phone/common/mail/store/imap/DigestMd5Utils.java b/src/com/android/phone/common/mail/store/imap/DigestMd5Utils.java
index e6376a3..f78dbdf 100644
--- a/src/com/android/phone/common/mail/store/imap/DigestMd5Utils.java
+++ b/src/com/android/phone/common/mail/store/imap/DigestMd5Utils.java
@@ -19,12 +19,12 @@
import android.annotation.Nullable;
import android.util.ArrayMap;
import android.util.Base64;
-import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.phone.common.mail.MailTransport;
import com.android.phone.common.mail.MessagingException;
import com.android.phone.common.mail.store.ImapStore;
+import com.android.phone.vvm.omtp.VvmLog;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
@@ -244,7 +244,7 @@
}
}
} catch (IndexOutOfBoundsException e) {
- Log.e(TAG, e.toString());
+ VvmLog.e(TAG, e.toString());
return null;
}
return mResult;
diff --git a/src/com/android/phone/common/mail/store/imap/ImapConstants.java b/src/com/android/phone/common/mail/store/imap/ImapConstants.java
index 9e6e247..a2eab13 100644
--- a/src/com/android/phone/common/mail/store/imap/ImapConstants.java
+++ b/src/com/android/phone/common/mail/store/imap/ImapConstants.java
@@ -98,6 +98,32 @@
public static final String NIL = "NIL";
/**
+ * NO responses
+ */
+ public static final String NO_COMMAND_NOT_ALLOWED = "command not allowed";
+ public static final String NO_RESERVATION_FAILED = "reservation failed";
+ public static final String NO_APPLICATION_ERROR = "application error";
+ public static final String NO_INVALID_PARAMETER = "invalid parameter";
+ public static final String NO_INVALID_COMMAND = "invalid command";
+ public static final String NO_UNKNOWN_COMMAND = "unknown command";
+ // AUTHENTICATE
+ // The subscriber can not be located in the system.
+ public static final String NO_UNKNOWN_USER = "unknown user";
+ // The Client Type or Protocol Version is unknown.
+ public static final String NO_UNKNOWN_CLIENT = "unknown client";
+ // The password received from the client does not match the password defined in the subscriber's profile.
+ public static final String NO_INVALID_PASSWORD = "invalid password";
+ // The subscriber's mailbox has not yet been initialised via the TUI
+ public static final String NO_MAILBOX_NOT_INITIALIZED = "mailbox not initialized";
+ // The subscriber has not been provisioned for the VVM service.
+ public static final String NO_SERVICE_IS_NOT_PROVISIONED =
+ "service is not provisioned";
+ // The subscriber is provisioned for the VVM service but the VVM service is currently not active
+ public static final String NO_SERVICE_IS_NOT_ACTIVATED = "service is not activated";
+ // The Voice Mail Blocked flag in the subscriber's profile is set to YES.
+ public static final String NO_USER_IS_BLOCKED = "user is blocked";
+
+ /**
* extensions
*/
public static final String GETQUOTA = "GETQUOTA";
@@ -105,11 +131,6 @@
public static final String QUOTAROOT = "QUOTAROOT";
public static final String QUOTA = "QUOTA";
- /** response codes within IMAP responses */
- public static final String EXPIRED = "EXPIRED";
- public static final String AUTHENTICATIONFAILED = "AUTHENTICATIONFAILED";
- public static final String UNAVAILABLE = "UNAVAILABLE";
-
/**
* capabilities
*/
diff --git a/src/com/android/phone/common/mail/store/imap/ImapMemoryLiteral.java b/src/com/android/phone/common/mail/store/imap/ImapMemoryLiteral.java
index aac66c2..4811590 100644
--- a/src/com/android/phone/common/mail/store/imap/ImapMemoryLiteral.java
+++ b/src/com/android/phone/common/mail/store/imap/ImapMemoryLiteral.java
@@ -16,9 +16,8 @@
package com.android.phone.common.mail.store.imap;
-import android.util.Log;
-
import com.android.phone.common.mail.FixedLengthInputStream;
+import com.android.phone.vvm.omtp.VvmLog;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -45,7 +44,7 @@
pos += read;
}
if (pos != mData.length) {
- Log.w(TAG, "");
+ VvmLog.w(TAG, "length mismatch");
}
}
@@ -60,7 +59,7 @@
try {
return new String(mData, "US-ASCII");
} catch (UnsupportedEncodingException e) {
- Log.e(TAG, "Unsupported encoding: ", e);
+ VvmLog.e(TAG, "Unsupported encoding: ", e);
}
return null;
}
diff --git a/src/com/android/phone/common/mail/store/imap/ImapResponseParser.java b/src/com/android/phone/common/mail/store/imap/ImapResponseParser.java
index d0413df..a6d2df6 100644
--- a/src/com/android/phone/common/mail/store/imap/ImapResponseParser.java
+++ b/src/com/android/phone/common/mail/store/imap/ImapResponseParser.java
@@ -22,6 +22,7 @@
import com.android.phone.common.mail.FixedLengthInputStream;
import com.android.phone.common.mail.MessagingException;
import com.android.phone.common.mail.PeekableInputStream;
+import com.android.phone.vvm.omtp.VvmLog;
import java.io.IOException;
import java.io.InputStream;
@@ -83,9 +84,7 @@
private static IOException newEOSException() {
final String message = "End of stream reached";
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, message);
- }
+ VvmLog.d(TAG, message);
return new IOException(message);
}
@@ -144,9 +143,6 @@
ImapResponse response = null;
try {
response = parseResponse();
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "<<< " + response.toString());
- }
} catch (RuntimeException e) {
// Parser crash -- log network activities.
onParseError(e);
@@ -183,7 +179,7 @@
}
} catch (IOException ignore) {
}
- Log.w(TAG, "Exception detected: " + e.getMessage());
+ VvmLog.w(TAG, "Exception detected: " + e.getMessage());
}
/**
diff --git a/src/com/android/phone/common/mail/store/imap/ImapSimpleString.java b/src/com/android/phone/common/mail/store/imap/ImapSimpleString.java
index 3d5263b..9d65236 100644
--- a/src/com/android/phone/common/mail/store/imap/ImapSimpleString.java
+++ b/src/com/android/phone/common/mail/store/imap/ImapSimpleString.java
@@ -16,7 +16,7 @@
package com.android.phone.common.mail.store.imap;
-import android.util.Log;
+import com.android.phone.vvm.omtp.VvmLog;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
@@ -49,7 +49,7 @@
try {
return new ByteArrayInputStream(mString.getBytes("US-ASCII"));
} catch (UnsupportedEncodingException e) {
- Log.e(TAG, "Unsupported encoding: ", e);
+ VvmLog.e(TAG, "Unsupported encoding: ", e);
}
return null;
}
diff --git a/src/com/android/phone/common/mail/store/imap/ImapString.java b/src/com/android/phone/common/mail/store/imap/ImapString.java
index a33ba24..dd7133c 100644
--- a/src/com/android/phone/common/mail/store/imap/ImapString.java
+++ b/src/com/android/phone/common/mail/store/imap/ImapString.java
@@ -16,7 +16,7 @@
package com.android.phone.common.mail.store.imap;
-import android.util.Log;
+import com.android.phone.vvm.omtp.VvmLog;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
@@ -137,7 +137,7 @@
mParsedDate = DATE_TIME_FORMAT.parse(getString());
return true;
} catch (ParseException e) {
- Log.w("ImapString", getString() + " can't be parsed as a date.");
+ VvmLog.w("ImapString", getString() + " can't be parsed as a date.");
return false;
}
}
diff --git a/src/com/android/phone/common/mail/utils/LogUtils.java b/src/com/android/phone/common/mail/utils/LogUtils.java
index 711af9b..6bd7be6 100644
--- a/src/com/android/phone/common/mail/utils/LogUtils.java
+++ b/src/com/android/phone/common/mail/utils/LogUtils.java
@@ -20,6 +20,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.phone.vvm.omtp.VvmLog;
import java.util.List;
import java.util.regex.Pattern;
@@ -184,7 +185,7 @@
*/
public static int v(String tag, String format, Object... args) {
if (isLoggable(tag, VERBOSE)) {
- return Log.v(tag, String.format(format, args));
+ return VvmLog.v(tag, String.format(format, args));
}
return 0;
}
@@ -202,7 +203,7 @@
*/
public static int v(String tag, Throwable tr, String format, Object... args) {
if (isLoggable(tag, VERBOSE)) {
- return Log.v(tag, String.format(format, args), tr);
+ return VvmLog.v(tag, String.format(format, args), tr);
}
return 0;
}
@@ -219,7 +220,7 @@
*/
public static int d(String tag, String format, Object... args) {
if (isLoggable(tag, DEBUG)) {
- return Log.d(tag, String.format(format, args));
+ return VvmLog.d(tag, String.format(format, args));
}
return 0;
}
@@ -237,7 +238,7 @@
*/
public static int d(String tag, Throwable tr, String format, Object... args) {
if (isLoggable(tag, DEBUG)) {
- return Log.d(tag, String.format(format, args), tr);
+ return VvmLog.d(tag, String.format(format, args), tr);
}
return 0;
}
@@ -254,7 +255,7 @@
*/
public static int i(String tag, String format, Object... args) {
if (isLoggable(tag, INFO)) {
- return Log.i(tag, String.format(format, args));
+ return VvmLog.i(tag, String.format(format, args));
}
return 0;
}
@@ -272,7 +273,7 @@
*/
public static int i(String tag, Throwable tr, String format, Object... args) {
if (isLoggable(tag, INFO)) {
- return Log.i(tag, String.format(format, args), tr);
+ return VvmLog.i(tag, String.format(format, args), tr);
}
return 0;
}
@@ -289,7 +290,7 @@
*/
public static int w(String tag, String format, Object... args) {
if (isLoggable(tag, WARN)) {
- return Log.w(tag, String.format(format, args));
+ return VvmLog.w(tag, String.format(format, args));
}
return 0;
}
@@ -307,7 +308,7 @@
*/
public static int w(String tag, Throwable tr, String format, Object... args) {
if (isLoggable(tag, WARN)) {
- return Log.w(tag, String.format(format, args), tr);
+ return VvmLog.w(tag, String.format(format, args), tr);
}
return 0;
}
@@ -324,7 +325,7 @@
*/
public static int e(String tag, String format, Object... args) {
if (isLoggable(tag, ERROR)) {
- return Log.e(tag, String.format(format, args));
+ return VvmLog.e(tag, String.format(format, args));
}
return 0;
}
@@ -342,7 +343,7 @@
*/
public static int e(String tag, Throwable tr, String format, Object... args) {
if (isLoggable(tag, ERROR)) {
- return Log.e(tag, String.format(format, args), tr);
+ return VvmLog.e(tag, String.format(format, args), tr);
}
return 0;
}
@@ -362,7 +363,7 @@
* additional arguments are ignored.
*/
public static int wtf(String tag, String format, Object... args) {
- return Log.wtf(tag, String.format(format, args), new Error());
+ return VvmLog.wtf(tag, String.format(format, args), new Error());
}
/**
@@ -381,7 +382,7 @@
* additional arguments are ignored.
*/
public static int wtf(String tag, Throwable tr, String format, Object... args) {
- return Log.wtf(tag, String.format(format, args), tr);
+ return VvmLog.wtf(tag, String.format(format, args), tr);
}
diff --git a/src/com/android/phone/settings/VisualVoicemailSettingsUtil.java b/src/com/android/phone/settings/VisualVoicemailSettingsUtil.java
index 62abffd..d7e573e 100644
--- a/src/com/android/phone/settings/VisualVoicemailSettingsUtil.java
+++ b/src/com/android/phone/settings/VisualVoicemailSettingsUtil.java
@@ -22,8 +22,11 @@
import com.android.internal.telephony.Phone;
import com.android.phone.PhoneUtils;
+import com.android.phone.R;
import com.android.phone.vvm.omtp.OmtpConstants;
+import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
import com.android.phone.vvm.omtp.sms.StatusMessage;
+import com.android.phone.vvm.omtp.utils.PhoneAccountHandleConverter;
/**
* Save visual voicemail login values and whether or not a particular account is enabled in shared
@@ -36,9 +39,6 @@
"visual_voicemail_";
private static final String IS_ENABLED_KEY = "is_enabled";
- // If a carrier vvm app is installed, Google visual voicemail is automatically switched off
- // however, the user can override this setting.
- private static final String IS_USER_SET = "is_user_set";
// Record the timestamp of the last full sync so that duplicate syncs can be reduced.
private static final String LAST_FULL_SYNC_TIMESTAMP = "last_full_sync_timestamp";
// Constant indicating that there has never been a full sync.
@@ -49,23 +49,13 @@
private static final long MAX_SYNC_RETRY_INTERVAL_MS = 86400000; // 24 hours
private static final long DEFAULT_SYNC_RETRY_INTERVAL_MS = 900000; // 15 minutes
-
- public static void setVisualVoicemailEnabled(Phone phone, boolean isEnabled,
- boolean isUserSet) {
- setVisualVoicemailEnabled(phone.getContext(), PhoneUtils.makePstnPhoneAccountHandle(phone),
- isEnabled, isUserSet);
- }
-
public static void setVisualVoicemailEnabled(Context context, PhoneAccountHandle phoneAccount,
- boolean isEnabled, boolean isUserSet) {
+ boolean isEnabled) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putBoolean(
- getVisualVoicemailSharedPrefsKey(IS_ENABLED_KEY, phoneAccount), isEnabled);
- editor.putBoolean(
- getVisualVoicemailSharedPrefsKey(IS_USER_SET, phoneAccount),
- isUserSet);
- editor.commit();
+ prefs.edit()
+ .putBoolean(getVisualVoicemailSharedPrefsKey(IS_ENABLED_KEY, phoneAccount),
+ isEnabled)
+ .apply();
}
public static boolean isVisualVoicemailEnabled(Context context,
@@ -73,9 +63,19 @@
if (phoneAccount == null) {
return false;
}
+ if (!context.getResources().getBoolean(R.bool.allow_visual_voicemail)) {
+ return false;
+ }
+
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- return prefs.getBoolean(getVisualVoicemailSharedPrefsKey(IS_ENABLED_KEY, phoneAccount),
- false);
+ String key = getVisualVoicemailSharedPrefsKey(IS_ENABLED_KEY, phoneAccount);
+ if (prefs.contains(key)) {
+ // isEnableByDefault is a bit expensive, so don't use it as default value of
+ // getBoolean(). The "false" here should never be actually used.
+ return prefs.getBoolean(key, false);
+ }
+ return new OmtpVvmCarrierConfigHelper(context,
+ PhoneAccountHandleConverter.toSubId(phoneAccount)).isEnabledByDefault();
}
public static boolean isVisualVoicemailEnabled(Phone phone) {
@@ -84,9 +84,10 @@
}
/**
- * Differentiate user-enabled/disabled to know whether to ignore automatic enabling and
- * disabling by the system. This is relevant when a carrier vvm app is installed and the user
- * manually enables dialer visual voicemail. In that case we would want that setting to persist.
+ * Whether the client enabled status is explicitly set by user or by default(Whether carrier VVM
+ * app is installed). This is used to determine whether to disable the client when the carrier
+ * VVM app is installed. If the carrier VVM app is installed the client should give priority to
+ * it if the settings are not touched.
*/
public static boolean isVisualVoicemailUserSet(Context context,
PhoneAccountHandle phoneAccount) {
@@ -94,9 +95,7 @@
return false;
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- return prefs.getBoolean(
- getVisualVoicemailSharedPrefsKey(IS_USER_SET, phoneAccount),
- false);
+ return prefs.contains(getVisualVoicemailSharedPrefsKey(IS_ENABLED_KEY, phoneAccount));
}
public static void setVisualVoicemailCredentialsFromStatusMessage(Context context,
diff --git a/src/com/android/phone/settings/VoicemailChangePinDialogPreference.java b/src/com/android/phone/settings/VoicemailChangePinDialogPreference.java
index d960dc4..e4230ff 100644
--- a/src/com/android/phone/settings/VoicemailChangePinDialogPreference.java
+++ b/src/com/android/phone/settings/VoicemailChangePinDialogPreference.java
@@ -35,6 +35,7 @@
import com.android.phone.common.mail.MessagingException;
import com.android.phone.vvm.omtp.OmtpConstants;
import com.android.phone.vvm.omtp.OmtpConstants.ChangePinResult;
+import com.android.phone.vvm.omtp.OmtpEvents;
import com.android.phone.vvm.omtp.imap.ImapHelper;
import com.android.phone.vvm.omtp.sync.VvmNetworkRequestCallback;
@@ -192,6 +193,7 @@
// Wipe the default old PIN so the old PIN input box will be shown to the user
// on the next time.
setDefaultOldPIN(mContext, mPhoneAccountHandle, null);
+ helper.handleEvent(OmtpEvents.CONFIG_PIN_SET);
}
} catch (MessagingException e) {
finishPinChange();
diff --git a/src/com/android/phone/settings/VoicemailRingtonePreference.java b/src/com/android/phone/settings/VoicemailRingtonePreference.java
index 4ee4e64..bb82d4f 100644
--- a/src/com/android/phone/settings/VoicemailRingtonePreference.java
+++ b/src/com/android/phone/settings/VoicemailRingtonePreference.java
@@ -17,8 +17,13 @@
* it is created or updated.
*/
public class VoicemailRingtonePreference extends RingtonePreference {
+ public interface VoicemailRingtoneNameChangeListener {
+ void onVoicemailRingtoneNameChanged(CharSequence name);
+ }
+
private static final int MSG_UPDATE_VOICEMAIL_RINGTONE_SUMMARY = 1;
+ private VoicemailRingtoneNameChangeListener mVoicemailRingtoneNameChangeListener;
private Runnable mVoicemailRingtoneLookupRunnable;
private Handler mVoicemailRingtoneLookupComplete;
@@ -32,6 +37,10 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_VOICEMAIL_RINGTONE_SUMMARY:
+ if (mVoicemailRingtoneNameChangeListener != null) {
+ mVoicemailRingtoneNameChangeListener.onVoicemailRingtoneNameChanged(
+ (CharSequence) msg.obj);
+ }
setSummary((CharSequence) msg.obj);
break;
}
@@ -39,7 +48,7 @@
};
}
- public void init(Phone phone) {
+ public void init(Phone phone, CharSequence oldRingtoneName) {
mPhone = phone;
// Requesting the ringtone will trigger migration if necessary.
@@ -48,6 +57,7 @@
final Preference preference = this;
final String preferenceKey =
VoicemailNotificationSettingsUtil.getVoicemailRingtoneSharedPrefsKey(mPhone);
+ setSummary(oldRingtoneName);
mVoicemailRingtoneLookupRunnable = new Runnable() {
@Override
public void run() {
@@ -63,6 +73,10 @@
updateRingtoneName();
}
+ public void setVoicemailRingtoneNameChangeListener(VoicemailRingtoneNameChangeListener l) {
+ mVoicemailRingtoneNameChangeListener = l;
+ }
+
@Override
protected Uri onRestoreRingtone() {
return VoicemailNotificationSettingsUtil.getRingtoneUri(mPhone);
diff --git a/src/com/android/phone/settings/VoicemailSettingsActivity.java b/src/com/android/phone/settings/VoicemailSettingsActivity.java
index fc53f15..b10af6e 100644
--- a/src/com/android/phone/settings/VoicemailSettingsActivity.java
+++ b/src/com/android/phone/settings/VoicemailSettingsActivity.java
@@ -30,6 +30,7 @@
import android.preference.PreferenceScreen;
import android.preference.SwitchPreference;
import android.provider.ContactsContract.CommonDataKinds;
+import android.telecom.PhoneAccountHandle;
import android.text.BidiFormatter;
import android.text.TextDirectionHeuristics;
import android.text.TextUtils;
@@ -58,7 +59,8 @@
implements DialogInterface.OnClickListener,
Preference.OnPreferenceChangeListener,
EditPhoneNumberPreference.OnDialogClosedListener,
- EditPhoneNumberPreference.GetDefaultNumberListener {
+ EditPhoneNumberPreference.GetDefaultNumberListener,
+ VoicemailRingtonePreference.VoicemailRingtoneNameChangeListener {
private static final String LOG_TAG = VoicemailSettingsActivity.class.getSimpleName();
private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
@@ -185,6 +187,8 @@
private CallForwardInfo[] mNewFwdSettings;
private String mNewVMNumber;
+ private CharSequence mOldVmRingtoneName = "";
+
/**
* Used to indicate that the voicemail preference should be shown.
*/
@@ -252,7 +256,8 @@
mVoicemailNotificationRingtone = (VoicemailRingtonePreference) findPreference(
getResources().getString(R.string.voicemail_notification_ringtone_key));
- mVoicemailNotificationRingtone.init(mPhone);
+ mVoicemailNotificationRingtone.setVoicemailRingtoneNameChangeListener(this);
+ mVoicemailNotificationRingtone.init(mPhone, mOldVmRingtoneName);
mVoicemailNotificationVibrate = (CheckBoxPreference) findPreference(
getResources().getString(R.string.voicemail_notification_vibrate_key));
@@ -397,8 +402,10 @@
VoicemailNotificationSettingsUtil.setVibrationEnabled(
mPhone, Boolean.TRUE.equals(objValue));
} else if (preference.getKey().equals(mVoicemailVisualVoicemail.getKey())) {
- boolean isEnabled = (Boolean) objValue;
- VisualVoicemailSettingsUtil.setVisualVoicemailEnabled(mPhone, isEnabled, true);
+ boolean isEnabled = (boolean) objValue;
+ PhoneAccountHandle handle = PhoneUtils.makePstnPhoneAccountHandle(mPhone);
+ VisualVoicemailSettingsUtil
+ .setVisualVoicemailEnabled(mPhone.getContext(), handle, isEnabled);
PreferenceScreen prefSet = getPreferenceScreen();
if (isEnabled) {
OmtpVvmSourceManager.getInstance(mPhone.getContext()).addPhoneStateListener(mPhone);
@@ -538,6 +545,11 @@
super.onActivityResult(requestCode, resultCode, data);
}
+ @Override
+ public void onVoicemailRingtoneNameChanged(CharSequence name) {
+ mOldVmRingtoneName = name;
+ }
+
/**
* Simulates user clicking on a passed preference.
* Usually needed when the preference is a dialog preference and we want to invoke
diff --git a/src/com/android/phone/vvm/omtp/DefaultOmtpEventHandler.java b/src/com/android/phone/vvm/omtp/DefaultOmtpEventHandler.java
index 6816d4c..c49df64 100644
--- a/src/com/android/phone/vvm/omtp/DefaultOmtpEventHandler.java
+++ b/src/com/android/phone/vvm/omtp/DefaultOmtpEventHandler.java
@@ -22,74 +22,75 @@
import com.android.phone.VoicemailStatus;
import com.android.phone.vvm.omtp.OmtpEvents.Type;
-import com.android.services.telephony.Log;
public class DefaultOmtpEventHandler {
private static final String TAG = "DefErrorCodeHandler";
- public static void handleEvent(Context context, int subId, OmtpEvents event) {
+ public static void handleEvent(Context context, OmtpVvmCarrierConfigHelper config,
+ OmtpEvents event) {
switch (event.getType()) {
case Type.CONFIGURATION:
- handleConfigurationEvent(context, subId, event);
+ handleConfigurationEvent(context, config, event);
break;
case Type.DATA_CHANNEL:
- handleDataChannelEvent(context, subId, event);
+ handleDataChannelEvent(context, config, event);
break;
case Type.NOTIFICATION_CHANNEL:
- handleNotificationChannelEvent(context, subId, event);
+ handleNotificationChannelEvent(context, config, event);
break;
case Type.OTHER:
- handleOtherEvent(context, subId, event);
+ handleOtherEvent(context, config, event);
break;
default:
- Log.wtf(TAG, "invalid event type " + event.getType() + " for " + event);
+ VvmLog.wtf(TAG, "invalid event type " + event.getType() + " for " + event);
}
}
- private static void handleConfigurationEvent(Context context, int subId,
+ private static void handleConfigurationEvent(Context context, OmtpVvmCarrierConfigHelper config,
OmtpEvents event) {
switch (event) {
case CONFIG_REQUEST_STATUS_SUCCESS:
- VoicemailStatus.edit(context, subId)
+ case CONFIG_PIN_SET:
+ VoicemailStatus.edit(context, config.getSubId())
.setConfigurationState(VoicemailContract.Status.CONFIGURATION_STATE_OK)
.setNotificationChannelState(Status.NOTIFICATION_CHANNEL_STATE_OK)
.apply();
break;
default:
- Log.wtf(TAG, "invalid configuration event " + event);
+ VvmLog.wtf(TAG, "invalid configuration event " + event);
}
}
- private static void handleDataChannelEvent(Context context, int subId,
+ private static void handleDataChannelEvent(Context context, OmtpVvmCarrierConfigHelper config,
OmtpEvents event) {
switch (event) {
case DATA_IMAP_OPERATION_COMPLETED:
- VoicemailStatus.edit(context, subId)
+ VoicemailStatus.edit(context, config.getSubId())
.setDataChannelState(Status.DATA_CHANNEL_STATE_OK)
.apply();
break;
case DATA_NO_CONNECTION:
- VoicemailStatus.edit(context, subId)
+ VoicemailStatus.edit(context, config.getSubId())
.setDataChannelState(Status.DATA_CHANNEL_STATE_NO_CONNECTION)
.apply();
break;
case DATA_NO_CONNECTION_CELLULAR_REQUIRED:
- VoicemailStatus.edit(context, subId)
+ VoicemailStatus.edit(context, config.getSubId())
.setDataChannelState(
Status.DATA_CHANNEL_STATE_NO_CONNECTION_CELLULAR_REQUIRED)
.apply();
break;
case DATA_INVALID_PORT:
- VoicemailStatus.edit(context, subId)
+ VoicemailStatus.edit(context, config.getSubId())
.setDataChannelState(
VoicemailContract.Status.DATA_CHANNEL_STATE_BAD_CONFIGURATION)
.apply();
break;
case DATA_CANNOT_RESOLVE_HOST_ON_NETWORK:
- VoicemailStatus.edit(context, subId)
+ VoicemailStatus.edit(context, config.getSubId())
.setDataChannelState(
VoicemailContract.Status.DATA_CHANNEL_STATE_SERVER_CONNECTION_ERROR)
.apply();
@@ -97,13 +98,20 @@
case DATA_SSL_INVALID_HOST_NAME:
case DATA_CANNOT_ESTABLISH_SSL_SESSION:
case DATA_IOE_ON_OPEN:
- VoicemailStatus.edit(context, subId)
+ VoicemailStatus.edit(context, config.getSubId())
.setDataChannelState(
VoicemailContract.Status.DATA_CHANNEL_STATE_COMMUNICATION_ERROR)
.apply();
break;
case DATA_BAD_IMAP_CREDENTIAL:
- VoicemailStatus.edit(context, subId)
+ case DATA_AUTH_UNKNOWN_USER:
+ case DATA_AUTH_UNKNOWN_DEVICE:
+ case DATA_AUTH_INVALID_PASSWORD:
+ case DATA_AUTH_MAILBOX_NOT_INITIALIZED:
+ case DATA_AUTH_SERVICE_NOT_PROVISIONED:
+ case DATA_AUTH_SERVICE_NOT_ACTIVATED:
+ case DATA_AUTH_USER_IS_BLOCKED:
+ VoicemailStatus.edit(context, config.getSubId())
.setDataChannelState(
VoicemailContract.Status.DATA_CHANNEL_STATE_BAD_CONFIGURATION)
.apply();
@@ -113,40 +121,44 @@
case DATA_INVALID_INITIAL_SERVER_RESPONSE:
case DATA_SSL_EXCEPTION:
case DATA_ALL_SOCKET_CONNECTION_FAILED:
- VoicemailStatus.edit(context, subId)
+ VoicemailStatus.edit(context, config.getSubId())
.setDataChannelState(
VoicemailContract.Status.DATA_CHANNEL_STATE_SERVER_ERROR)
.apply();
break;
default:
- Log.wtf(TAG, "invalid data channel event " + event);
+ VvmLog.wtf(TAG, "invalid data channel event " + event);
}
}
- private static void handleNotificationChannelEvent(Context context, int subId,
- OmtpEvents event) {
+ private static void handleNotificationChannelEvent(Context context,
+ OmtpVvmCarrierConfigHelper config, OmtpEvents event) {
switch (event) {
case NOTIFICATION_IN_SERVICE:
- VoicemailStatus.edit(context, subId)
+ VoicemailStatus.edit(context, config.getSubId())
.setNotificationChannelState(Status.NOTIFICATION_CHANNEL_STATE_OK)
.apply();
break;
case NOTIFICATION_SERVICE_LOST:
- VoicemailStatus.edit(context, subId)
- .setNotificationChannelState(
- Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION)
- .apply();
+ VoicemailStatus.Editor editor = VoicemailStatus.edit(context, config.getSubId());
+ editor.setNotificationChannelState(Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION);
+ if (config.isCellularDataRequired()) {
+ editor.setDataChannelState(
+ Status.DATA_CHANNEL_STATE_NO_CONNECTION_CELLULAR_REQUIRED);
+ }
+ editor.apply();
break;
default:
- Log.wtf(TAG, "invalid notification channel event " + event);
+ VvmLog.wtf(TAG, "invalid notification channel event " + event);
}
}
- private static void handleOtherEvent(Context context, int subId, OmtpEvents event) {
+ private static void handleOtherEvent(Context context, OmtpVvmCarrierConfigHelper config,
+ OmtpEvents event) {
switch (event) {
case OTHER_SOURCE_REMOVED:
- VoicemailStatus.edit(context, subId)
+ VoicemailStatus.edit(context, config.getSubId())
.setConfigurationState(Status.CONFIGURATION_STATE_NOT_CONFIGURED)
.setNotificationChannelState(
Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION)
@@ -154,7 +166,7 @@
.apply();
break;
default:
- Log.wtf(TAG, "invalid other event " + event);
+ VvmLog.wtf(TAG, "invalid other event " + event);
}
}
}
diff --git a/src/com/android/phone/vvm/omtp/LocalLogHelper.java b/src/com/android/phone/vvm/omtp/LocalLogHelper.java
deleted file mode 100644
index a3de74f..0000000
--- a/src/com/android/phone/vvm/omtp/LocalLogHelper.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2015 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.vvm.omtp;
-
-import com.android.internal.telephony.PhoneFactory;
-
-/**
- * Helper methods for adding to Telephony local logs.
- */
-public class LocalLogHelper {
- public static final String KEY = "OmtpVvm";
- private static final int MAX_OMTP_VVM_LOGS = 20;
-
- public static void log(String tag, String log) {
- try {
- PhoneFactory.addLocalLog(KEY, MAX_OMTP_VVM_LOGS);
- } catch (IllegalArgumentException e){
- } finally {
- PhoneFactory.localLog(KEY, tag + ": " + log);
- }
- }
-}
diff --git a/src/com/android/phone/vvm/omtp/OmtpBootCompletedReceiver.java b/src/com/android/phone/vvm/omtp/OmtpBootCompletedReceiver.java
deleted file mode 100644
index a2b85f7..0000000
--- a/src/com/android/phone/vvm/omtp/OmtpBootCompletedReceiver.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2016 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.vvm.omtp;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-import android.util.ArraySet;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.Set;
-
-/**
- * Stores subscription ID of SIMs while the device is locked to process them after the device is
- * unlocked. This class is only intended to be used within {@link SimChangeReceiver}. subId is used
- * for Visual voicemail activation/deactivation, which need to be done when the device is unlocked.
- * But the enumeration of subIds happen on boot, when the device could be locked. This class is used
- * to defer all activation/deactivation until the device is unlocked.
- *
- * The subIds are stored in device encrypted {@link SharedPreferences} (readable/writable even
- * locked). after the device is unlocked the list is read and deleted.
- */
-public class OmtpBootCompletedReceiver extends BroadcastReceiver {
-
- private static final String TAG = "OmtpBootCompletedRcvr";
-
- private static final String DEFERRED_SUBID_LIST_KEY = "deferred_sub_id_key";
-
- @VisibleForTesting
- interface SubIdProcessor{
- void process(Context context,int subId);
- }
-
- private SubIdProcessor mSubIdProcessor = new SubIdProcessor() {
- @Override
- public void process(Context context, int subId) {
- SimChangeReceiver.processSubId(context,subId);
- }
- };
-
- /**
- * Write the subId to the the list.
- */
- public static void addDeferredSubId(Context context, int subId) {
- SharedPreferences sharedPreferences = getSubIdSharedPreference(context);
- Set<String> subIds =
- new ArraySet<>(sharedPreferences.getStringSet(DEFERRED_SUBID_LIST_KEY, null));
- subIds.add(String.valueOf(subId));
- sharedPreferences.edit().putStringSet(DEFERRED_SUBID_LIST_KEY, subIds).apply();
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- // Listens to android.intent.action.BOOT_COMPLETED
- if(!intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
- return;
- }
-
- Log.v(TAG, "processing deferred subId list");
- Set<Integer> subIds = readAndDeleteSubIds(context);
- for (Integer subId : subIds) {
- Log.v(TAG, "processing subId " + subId);
- mSubIdProcessor.process(context, subId);
- }
- }
-
- /**
- * Read all subId from the list to a unique integer set, and delete the preference.
- */
- private static Set<Integer> readAndDeleteSubIds(Context context) {
- SharedPreferences sharedPreferences = getSubIdSharedPreference(context);
- Set<String> subIdStrings = sharedPreferences.getStringSet(DEFERRED_SUBID_LIST_KEY, null);
- Set<Integer> subIds = new ArraySet<>();
- if(subIdStrings == null) {
- return subIds;
- }
- for(String string : subIdStrings){
- subIds.add(Integer.valueOf(string));
- }
- getSubIdSharedPreference(context).edit().remove(DEFERRED_SUBID_LIST_KEY).apply();
- return subIds;
- }
-
- @VisibleForTesting
- void setSubIdProcessorForTest(SubIdProcessor processor){
- mSubIdProcessor = processor;
- }
-
- private static SharedPreferences getSubIdSharedPreference(Context context) {
- return PreferenceManager
- .getDefaultSharedPreferences(context.createDeviceProtectedStorageContext());
- }
-}
diff --git a/src/com/android/phone/vvm/omtp/OmtpEvents.java b/src/com/android/phone/vvm/omtp/OmtpEvents.java
index 6de692e..f42db72 100644
--- a/src/com/android/phone/vvm/omtp/OmtpEvents.java
+++ b/src/com/android/phone/vvm/omtp/OmtpEvents.java
@@ -31,43 +31,55 @@
// Configuration State
CONFIG_REQUEST_STATUS_SUCCESS(Type.CONFIGURATION, true),
+ CONFIG_PIN_SET(Type.CONFIGURATION, true),
+ // The voicemail PIN is replaced with a generated PIN, user should change it.
+ CONFIG_DEFAULT_PIN_REPLACED(Type.CONFIGURATION, true),
+
// Data channel State
// Successfully downloaded/uploaded data from the server, which means the data channel is clear.
DATA_IMAP_OPERATION_COMPLETED(Type.DATA_CHANNEL, true),
-
// The port provided in the STATUS SMS is invalid.
- DATA_INVALID_PORT(Type.DATA_CHANNEL, false),
+ DATA_INVALID_PORT(Type.DATA_CHANNEL),
// No connection to the internet, and the carrier requires cellular data
- DATA_NO_CONNECTION_CELLULAR_REQUIRED(Type.DATA_CHANNEL, false),
+ DATA_NO_CONNECTION_CELLULAR_REQUIRED(Type.DATA_CHANNEL),
// No connection to the internet.
- DATA_NO_CONNECTION(Type.DATA_CHANNEL, false),
+ DATA_NO_CONNECTION(Type.DATA_CHANNEL),
// Address lookup for the server hostname failed. DNS error?
- DATA_CANNOT_RESOLVE_HOST_ON_NETWORK(Type.DATA_CHANNEL, false),
+ DATA_CANNOT_RESOLVE_HOST_ON_NETWORK(Type.DATA_CHANNEL),
// All destination address that resolves to the server hostname are rejected or timed out
- DATA_ALL_SOCKET_CONNECTION_FAILED(Type.DATA_CHANNEL, false),
+ DATA_ALL_SOCKET_CONNECTION_FAILED(Type.DATA_CHANNEL),
// Failed to establish SSL with the server, either with a direct SSL connection or by
// STARTTLS command
- DATA_CANNOT_ESTABLISH_SSL_SESSION(Type.DATA_CHANNEL, false),
+ DATA_CANNOT_ESTABLISH_SSL_SESSION(Type.DATA_CHANNEL),
// Identity of the server cannot be verified.
- DATA_SSL_INVALID_HOST_NAME(Type.DATA_CHANNEL, false),
+ DATA_SSL_INVALID_HOST_NAME(Type.DATA_CHANNEL),
// The server rejected our username/password
- DATA_BAD_IMAP_CREDENTIAL(Type.DATA_CHANNEL, false),
+ DATA_BAD_IMAP_CREDENTIAL(Type.DATA_CHANNEL),
+
+ DATA_AUTH_UNKNOWN_USER(Type.DATA_CHANNEL),
+ DATA_AUTH_UNKNOWN_DEVICE(Type.DATA_CHANNEL),
+ DATA_AUTH_INVALID_PASSWORD(Type.DATA_CHANNEL),
+ DATA_AUTH_MAILBOX_NOT_INITIALIZED(Type.DATA_CHANNEL),
+ DATA_AUTH_SERVICE_NOT_PROVISIONED(Type.DATA_CHANNEL),
+ DATA_AUTH_SERVICE_NOT_ACTIVATED(Type.DATA_CHANNEL),
+ DATA_AUTH_USER_IS_BLOCKED(Type.DATA_CHANNEL),
+
// A command to the server didn't result with an "OK" or continuation request
- DATA_REJECTED_SERVER_RESPONSE(Type.DATA_CHANNEL, false),
+ DATA_REJECTED_SERVER_RESPONSE(Type.DATA_CHANNEL),
// The server did not greet us with a "OK", possibly not a IMAP server.
- DATA_INVALID_INITIAL_SERVER_RESPONSE(Type.DATA_CHANNEL, false),
+ DATA_INVALID_INITIAL_SERVER_RESPONSE(Type.DATA_CHANNEL),
// An IOException occurred while trying to open an ImapConnection
// TODO: reduce scope
- DATA_IOE_ON_OPEN(Type.DATA_CHANNEL, false),
+ DATA_IOE_ON_OPEN(Type.DATA_CHANNEL),
// The SELECT command on a mailbox is rejected
- DATA_MAILBOX_OPEN_FAILED(Type.DATA_CHANNEL, false),
+ DATA_MAILBOX_OPEN_FAILED(Type.DATA_CHANNEL),
// An IOException has occurred
// TODO: reduce scope
- DATA_GENERIC_IMAP_IOE(Type.DATA_CHANNEL, false),
+ DATA_GENERIC_IMAP_IOE(Type.DATA_CHANNEL),
// An SslException has occurred while opening an ImapConnection
// TODO: reduce scope
- DATA_SSL_EXCEPTION(Type.DATA_CHANNEL, false),
+ DATA_SSL_EXCEPTION(Type.DATA_CHANNEL),
// Notification Channel
@@ -78,8 +90,20 @@
// Other
- OTHER_SOURCE_REMOVED(Type.OTHER, false);
+ OTHER_SOURCE_REMOVED(Type.OTHER, false),
+ // VVM3
+ VVM3_NEW_USER_SETUP_FAILED,
+ // Table 4. client internal error handling
+ VVM3_VMG_DNS_FAILURE,
+ VVM3_SPG_DNS_FAILURE,
+ VVM3_VMG_CONNECTION_FAILED,
+ VVM3_SPG_CONNECTION_FAILED,
+ VVM3_VMG_TIMEOUT,
+ VVM3_STATUS_SMS_TIMEOUT,
+
+ VVM3_SUBSCRIBER_PROVISIONED,
+ VVM3_SUBSCRIBER_BLOCKED;
public static class Type {
@@ -103,6 +127,15 @@
mIsSuccess = isSuccess;
}
+ OmtpEvents(int type) {
+ mType = type;
+ mIsSuccess = false;
+ }
+
+ OmtpEvents() {
+ mType = Type.OTHER;
+ mIsSuccess = false;
+ }
@Type.Values
public int getType() {
diff --git a/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java b/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java
index 798f5da..b570744 100644
--- a/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java
+++ b/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java
@@ -27,7 +27,6 @@
import android.telephony.VisualVoicemailSmsFilterSettings;
import android.text.TextUtils;
import android.util.ArraySet;
-import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.phone.VoicemailStatus;
@@ -74,6 +73,12 @@
"vvm_ssl_port_number_int";
/**
+ * @see #isLegacyModeEnabled()
+ */
+ static final String KEY_VVM_LEGACY_MODE_ENABLED_BOOL =
+ "vvm_legacy_mode_enabled_bool";
+
+ /**
* Ban a capability reported by the server from being used. The array of string should be a
* subset of the capabilities returned IMAP CAPABILITY command.
*
@@ -99,7 +104,7 @@
TelephonyManager telephonyManager =
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
mTelephonyConfig = new TelephonyVvmConfigManager(context.getResources())
- .getConfig(telephonyManager.getNetworkOperator(subId));
+ .getConfig(telephonyManager.getSimOperator(subId));
mVvmType = getVvmType();
mProtocol = VisualVoicemailProtocolFactory.create(mVvmType);
@@ -174,6 +179,10 @@
* so by checking if the carrier's voicemail app is installed.
*/
public boolean isEnabledByDefault() {
+ if (!isValid()) {
+ return false;
+ }
+
Set<String> carrierPackages = getCarrierVvmPackageNames();
if (carrierPackages == null) {
return true;
@@ -259,24 +268,46 @@
return "//VVM";
}
+ /**
+ * Should legacy mode be used when the OMTP VVM client is disabled?
+ *
+ * <p>Legacy mode is a mode that on the carrier side visual voicemail is still activated, but on
+ * the client side all network operations are disabled. SMSs are still monitored so a new
+ * message SYNC SMS will be translated to show a message waiting indicator, like traditional
+ * voicemails.
+ *
+ * <p>This is for carriers that does not support VVM deactivation so voicemail can continue to
+ * function without the data cost.
+ */
+ public boolean isLegacyModeEnabled() {
+ return (boolean) getValue(KEY_VVM_LEGACY_MODE_ENABLED_BOOL, false);
+ }
+
public void startActivation() {
VoicemailStatus.edit(mContext, mSubId)
.setType(getVvmType())
.apply();
- TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
- telephonyManager.enableVisualVoicemailSmsFilter(mSubId,
- new VisualVoicemailSmsFilterSettings.Builder().setClientPrefix(getClientPrefix())
- .build());
+ activateSmsFilter();
if (mProtocol != null) {
mProtocol.startActivation(this);
}
}
+ public void activateSmsFilter() {
+ TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
+ telephonyManager.enableVisualVoicemailSmsFilter(mSubId,
+ new VisualVoicemailSmsFilterSettings.Builder().setClientPrefix(getClientPrefix())
+ .build());
+ }
+
public void startDeactivation() {
- mContext.getSystemService(TelephonyManager.class)
- .disableVisualVoicemailSmsFilter(mSubId);
+ if (!isLegacyModeEnabled()) {
+ // SMS should still be filtered in legacy mode
+ mContext.getSystemService(TelephonyManager.class)
+ .disableVisualVoicemailSmsFilter(mSubId);
+ }
if (mProtocol != null) {
mProtocol.startDeactivation(this);
}
@@ -295,29 +326,48 @@
}
public void handleEvent(OmtpEvents event) {
+ VvmLog.i(TAG, "OmtpEvent:" + event);
if (mProtocol != null) {
- mProtocol.handleEvent(mContext, mSubId, event);
+ mProtocol.handleEvent(mContext, this, event);
}
}
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("OmtpVvmCarrierConfigHelper [");
+ builder.append("subId: ").append(getSubId())
+ .append(", carrierConfig: ").append(mCarrierConfig != null)
+ .append(", telephonyConfig: ").append(mTelephonyConfig != null)
+ .append(", type: ").append(getVvmType())
+ .append(", destinationNumber: ").append(getDestinationNumber())
+ .append(", applicationPort: ").append(getApplicationPort())
+ .append(", sslPort: ").append(getSslPort())
+ .append(", isEnabledByDefault: ").append(isEnabledByDefault())
+ .append(", isCellularDataRequired: ").append(isCellularDataRequired())
+ .append(", isPrefetchEnabled: ").append(isPrefetchEnabled())
+ .append(", isLegacyModeEnabled: ").append(isLegacyModeEnabled())
+ .append("]");
+ return builder.toString();
+ }
+
@Nullable
private PersistableBundle getCarrierConfig() {
if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
- Log.w(TAG, "Invalid subscriptionId or subscriptionId not provided in intent.");
+ VvmLog
+ .w(TAG, "Invalid subscriptionId or subscriptionId not provided in intent.");
return null;
}
CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
if (carrierConfigManager == null) {
- Log.w(TAG, "No carrier config service found.");
+ VvmLog.w(TAG, "No carrier config service found.");
return null;
}
PersistableBundle config = carrierConfigManager.getConfigForSubId(mSubId);
if (TextUtils.isEmpty(config.getString(CarrierConfigManager.KEY_VVM_TYPE_STRING))) {
- Log.w(TAG, "Carrier config missing VVM type, ignoring.");
return null;
}
return config;
diff --git a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
index 01d6bf9..f22711a 100644
--- a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
+++ b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
@@ -18,17 +18,18 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.os.UserHandle;
+import android.content.pm.IPackageManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserManager;
import android.telecom.PhoneAccountHandle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
-import android.util.Log;
+import android.telephony.TelephonyManager;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
-import com.android.phone.R;
import com.android.phone.settings.VisualVoicemailSettingsUtil;
import com.android.phone.vvm.omtp.sync.OmtpVvmSourceManager;
import com.android.phone.vvm.omtp.utils.PhoneAccountHandleConverter;
@@ -43,18 +44,13 @@
*/
public class SimChangeReceiver extends BroadcastReceiver {
- private static final String TAG = "SimChangeReceiver";
+ private static final String TAG = "VvmSimChangeReceiver";
@Override
public void onReceive(Context context, Intent intent) {
- if (UserHandle.myUserId() != UserHandle.USER_SYSTEM) {
- Log.v(TAG, "Received broadcast for user that is not system.");
- return;
- }
-
final String action = intent.getAction();
if (action == null) {
- Log.w(TAG, "Null action for intent.");
+ VvmLog.w(TAG, "Null action for intent.");
return;
}
@@ -62,7 +58,7 @@
case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(
intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE))) {
- Log.i(TAG, "Sim removed, removing inactive accounts");
+ VvmLog.i(TAG, "Sim removed, removing inactive accounts");
OmtpVvmSourceManager.getInstance(context).removeInactiveSources();
}
break;
@@ -71,14 +67,16 @@
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- Log.i(TAG, "Received SIM change for invalid subscription id.");
+ VvmLog.i(TAG, "Received SIM change for invalid subscription id.");
return;
}
-
- if (!UserManager.get(context).isUserUnlocked()) {
- OmtpBootCompletedReceiver.addDeferredSubId(context, subId);
- } else {
+ VvmLog.d(TAG, "Carrier config changed");
+ if (UserManager.get(context).isUserUnlocked() && !isCryptKeeperMode()) {
processSubId(context, subId);
+ } else {
+ VvmLog.d(TAG, "User locked, activation request delayed until unlock");
+ // After the device is unlocked, VvmBootCompletedReceiver will iterate through
+ // all call capable subIds, nothing need to be done here.
}
break;
}
@@ -90,26 +88,9 @@
if (carrierConfigHelper.isValid()) {
PhoneAccountHandle phoneAccount = PhoneAccountHandleConverter.fromSubId(subId);
- boolean isUserSet = VisualVoicemailSettingsUtil.isVisualVoicemailUserSet(
- context, phoneAccount);
- boolean isEnabledInSettings =
- VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(context,
- phoneAccount);
- boolean isSupported =
- context.getResources().getBoolean(R.bool.allow_visual_voicemail);
- boolean isEnabled = isSupported && (isUserSet ? isEnabledInSettings :
- carrierConfigHelper.isEnabledByDefault());
-
- if (!isUserSet) {
- // Preserve the previous setting for "isVisualVoicemailEnabled" if it is
- // set by the user, otherwise, set this value for the first time.
- VisualVoicemailSettingsUtil.setVisualVoicemailEnabled(context, phoneAccount,
- isEnabled, /** isUserSet */false);
- }
-
- if (isEnabled) {
- LocalLogHelper.log(TAG, "Sim state or carrier config changed: requesting"
- + " activation for " + phoneAccount.getId());
+ if (VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(context, phoneAccount)) {
+ VvmLog.i(TAG, "Sim state or carrier config changed: requesting"
+ + " activation for " + subId);
// Add a phone state listener so that changes to the communication channels
// can be recorded.
@@ -117,11 +98,35 @@
phoneAccount);
carrierConfigHelper.startActivation();
} else {
+ if (carrierConfigHelper.isLegacyModeEnabled()) {
+ // SMS still need to be filtered under legacy mode.
+ VvmLog.i(TAG, "activating SMS filter for legacy mode");
+ carrierConfigHelper.activateSmsFilter();
+ }
// It may be that the source was not registered to begin with but we want
// to run through the steps to remove the source just in case.
OmtpVvmSourceManager.getInstance(context).removeSource(phoneAccount);
- Log.v(TAG, "Sim change for disabled account.");
+ VvmLog.v(TAG, "Sim change for disabled account.");
}
+ } else {
+ String mccMnc = context.getSystemService(TelephonyManager.class).getSimOperator(subId);
+ VvmLog.d(TAG,
+ "visual voicemail not supported for carrier " + mccMnc + " on subId " + subId);
}
}
+
+ /**
+ * CryptKeeper mode is the pre-file based encryption locked state, when the user has selected
+ * "Require password to boot" and the device hasn't been unlocked yet during a reboot. {@link
+ * UserManager#isUserUnlocked()} will still return true in this mode, but storage in /data and
+ * all content providers will not be available(including SharedPreference).
+ */
+ private static boolean isCryptKeeperMode() {
+ try {
+ return IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
+ isOnlyCoreApps();
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
}
\ No newline at end of file
diff --git a/src/com/android/phone/vvm/omtp/VvmBootCompletedReceiver.java b/src/com/android/phone/vvm/omtp/VvmBootCompletedReceiver.java
new file mode 100644
index 0000000..fe1f4cb
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/VvmBootCompletedReceiver.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 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.vvm.omtp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+
+import com.android.phone.vvm.omtp.utils.PhoneAccountHandleConverter;
+
+/**
+ * Upon boot iterate through all callable phone account to activate visual voicemail. This happens
+ * after the device has been unlocked. {@link android.telephony.CarrierConfigManager#
+ * ACTION_CARRIER_CONFIG_CHANGED} can also trigger activation upon boot but it can happen before the
+ * device is unlocked and visual voicemail will not be activated.
+ *
+ * <p>TODO: An additional duplicated activation request will be sent as a result of this receiver,
+ * but similar issues is already covered in b/28730056 and a scheduling system should be used to
+ * resolve this.
+ */
+public class VvmBootCompletedReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "VvmBootCompletedRcvr";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Listens to android.intent.action.BOOT_COMPLETED
+ if (!intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
+ return;
+ }
+
+ VvmLog.v(TAG, "processing subId list");
+ for (PhoneAccountHandle handle : TelecomManager.from(context)
+ .getCallCapablePhoneAccounts()) {
+ int subId = PhoneAccountHandleConverter.toSubId(handle);
+ VvmLog.v(TAG, "processing subId " + subId);
+ SimChangeReceiver.processSubId(context, subId);
+ }
+ }
+}
diff --git a/src/com/android/phone/vvm/omtp/VvmLog.java b/src/com/android/phone/vvm/omtp/VvmLog.java
new file mode 100644
index 0000000..82d42af
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/VvmLog.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2015 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.vvm.omtp;
+
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Helper methods for adding to OMTP visual voicemail local logs.
+ */
+public class VvmLog {
+
+ private static final int MAX_OMTP_VVM_LOGS = 100;
+
+ private static final LocalLog sLocalLog = new LocalLog(MAX_OMTP_VVM_LOGS);
+
+ public static void log(String tag, String log) {
+ sLocalLog.log(tag + ": " + log);
+ }
+
+ public static void dump(FileDescriptor fd, PrintWriter printwriter, String[] args) {
+ IndentingPrintWriter indentingPrintWriter = new IndentingPrintWriter(printwriter, " ");
+ indentingPrintWriter.increaseIndent();
+ sLocalLog.dump(fd, indentingPrintWriter, args);
+ indentingPrintWriter.decreaseIndent();
+ }
+
+ public static int e(String tag, String log) {
+ log(tag, log);
+ return Log.e(tag, log);
+ }
+
+ public static int e(String tag, String log, Throwable e) {
+ log(tag, log + " " + e);
+ return Log.e(tag, log, e);
+ }
+
+ public static int w(String tag, String log) {
+ log(tag, log);
+ return Log.w(tag, log);
+ }
+
+ public static int w(String tag, String log, Throwable e) {
+ log(tag, log + " " + e);
+ return Log.w(tag, log, e);
+ }
+
+ public static int i(String tag, String log) {
+ log(tag, log);
+ return Log.i(tag, log);
+ }
+
+ public static int i(String tag, String log, Throwable e) {
+ log(tag, log + " " + e);
+ return Log.i(tag, log, e);
+ }
+
+ public static int d(String tag, String log) {
+ log(tag, log);
+ return Log.d(tag, log);
+ }
+
+ public static int d(String tag, String log, Throwable e) {
+ log(tag, log + " " + e);
+ return Log.d(tag, log, e);
+ }
+
+ public static int v(String tag, String log) {
+ log(tag, log);
+ return Log.v(tag, log);
+ }
+
+ public static int v(String tag, String log, Throwable e) {
+ log(tag, log + " " + e);
+ return Log.v(tag, log, e);
+ }
+
+ public static int wtf(String tag, String log) {
+ log(tag, log);
+ return Log.wtf(tag, log);
+ }
+
+ public static int wtf(String tag, String log, Throwable e) {
+ log(tag, log + " " + e);
+ return Log.wtf(tag, log, e);
+ }
+}
diff --git a/src/com/android/phone/vvm/omtp/VvmPackageInstallReceiver.java b/src/com/android/phone/vvm/omtp/VvmPackageInstallReceiver.java
index 8d438ef..8a0495b 100644
--- a/src/com/android/phone/vvm/omtp/VvmPackageInstallReceiver.java
+++ b/src/com/android/phone/vvm/omtp/VvmPackageInstallReceiver.java
@@ -31,6 +31,9 @@
* enabled dialer vvm sources.
*/
public class VvmPackageInstallReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "VvmPkgInstallReceiver";
+
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getData() == null) {
@@ -56,8 +59,10 @@
continue;
}
if (carrierConfigHelper.getCarrierVvmPackageNames().contains(packageName)) {
- VisualVoicemailSettingsUtil.setVisualVoicemailEnabled(
- context, phoneAccount, false, false);
+ // Force deactivate the client. The user can re-enable it in the settings.
+ // There are no need to update the settings for deactivation. At this point, if the
+ // default value is used it should be false because a carrier package is present.
+ VvmLog.i(TAG, "Carrier VVM package installed, disabling system VVM client");
OmtpVvmSourceManager.getInstance(context).removeSource(phoneAccount);
carrierConfigHelper.startDeactivation();
}
diff --git a/src/com/android/phone/vvm/omtp/VvmPhoneStateListener.java b/src/com/android/phone/vvm/omtp/VvmPhoneStateListener.java
index 3438de2..64b37c6 100644
--- a/src/com/android/phone/vvm/omtp/VvmPhoneStateListener.java
+++ b/src/com/android/phone/vvm/omtp/VvmPhoneStateListener.java
@@ -20,7 +20,6 @@
import android.telecom.PhoneAccountHandle;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
-import android.util.Log;
import com.android.phone.PhoneGlobals;
import com.android.phone.PhoneUtils;
@@ -65,16 +64,16 @@
new VoicemailStatusQueryHelper(mContext);
if (voicemailStatusQueryHelper.isVoicemailSourceConfigured(mPhoneAccount)) {
if (!voicemailStatusQueryHelper.isNotificationsChannelActive(mPhoneAccount)) {
- Log.v(TAG, "Notifications channel is active for " + mPhoneAccount.getId());
+ VvmLog
+ .v(TAG, "Notifications channel is active for " + subId);
helper.handleEvent(OmtpEvents.NOTIFICATION_IN_SERVICE);
PhoneGlobals.getInstance().clearMwiIndicator(subId);
}
}
if (OmtpVvmSourceManager.getInstance(mContext).isVvmSourceRegistered(mPhoneAccount)) {
- Log.v(TAG, "Signal returned: requesting resync for " + mPhoneAccount.getId());
- LocalLogHelper.log(TAG,
- "Signal returned: requesting resync for " + mPhoneAccount.getId());
+ VvmLog
+ .v(TAG, "Signal returned: requesting resync for " + subId);
// If the source is already registered, run a full sync in case something was missed
// while signal was down.
Intent serviceIntent = OmtpVvmSyncService.getSyncIntent(
@@ -82,16 +81,15 @@
true /* firstAttempt */);
mContext.startService(serviceIntent);
} else {
- Log.v(TAG, "Signal returned: reattempting activation for " + mPhoneAccount.getId());
- LocalLogHelper.log(TAG,
- "Signal returned: reattempting activation for " + mPhoneAccount.getId());
+ VvmLog.v(TAG,
+ "Signal returned: reattempting activation for " + subId);
// Otherwise initiate an activation because this means that an OMTP source was
// recognized but either the activation text was not successfully sent or a response
// was not received.
helper.startActivation();
}
} else {
- Log.v(TAG, "Notifications channel is inactive for " + mPhoneAccount.getId());
+ VvmLog.v(TAG, "Notifications channel is inactive for " + subId);
mContext.stopService(OmtpVvmSyncService.getSyncIntent(
mContext, OmtpVvmSyncService.SYNC_FULL_SYNC, mPhoneAccount,
true /* firstAttempt */));
diff --git a/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java b/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
index 0095f53..fe3911c 100644
--- a/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
+++ b/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
@@ -30,10 +30,10 @@
import android.telecom.PhoneAccountHandle;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import android.util.Log;
import com.android.phone.PhoneUtils;
import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
import com.android.phone.vvm.omtp.imap.ImapHelper;
import com.android.phone.vvm.omtp.sync.OmtpVvmSourceManager;
import com.android.phone.vvm.omtp.sync.VvmNetworkRequestCallback;
@@ -45,7 +45,7 @@
private static final String TAG = "FetchVoicemailReceiver";
- final static String[] PROJECTION = new String[] {
+ final static String[] PROJECTION = new String[]{
Voicemails.SOURCE_DATA, // 0
Voicemails.PHONE_ACCOUNT_ID, // 1
Voicemails.PHONE_ACCOUNT_COMPONENT_NAME, // 2
@@ -74,23 +74,28 @@
@Override
public void onReceive(final Context context, Intent intent) {
if (VoicemailContract.ACTION_FETCH_VOICEMAIL.equals(intent.getAction())) {
+ VvmLog.i(TAG, "ACTION_FETCH_VOICEMAIL received");
mContext = context;
mContentResolver = context.getContentResolver();
mUri = intent.getData();
if (mUri == null) {
- Log.w(TAG, VoicemailContract.ACTION_FETCH_VOICEMAIL + " intent sent with no data");
+ VvmLog.w(TAG,
+ VoicemailContract.ACTION_FETCH_VOICEMAIL + " intent sent with no data");
return;
}
if (!context.getPackageName().equals(
mUri.getQueryParameter(VoicemailContract.PARAM_KEY_SOURCE_PACKAGE))) {
// Ignore if the fetch request is for a voicemail not from this package.
+ VvmLog.e(TAG,
+ "ACTION_FETCH_VOICEMAIL from foreign pacakge " + context.getPackageName());
return;
}
Cursor cursor = mContentResolver.query(mUri, PROJECTION, null, null, null);
if (cursor == null) {
+ VvmLog.i(TAG, "ACTION_FETCH_VOICEMAIL query returned null");
return;
}
try {
@@ -103,7 +108,7 @@
accountId = telephonyManager.getSimSerialNumber();
if (TextUtils.isEmpty(accountId)) {
- Log.e(TAG, "Account null and no default sim found.");
+ VvmLog.e(TAG, "Account null and no default sim found.");
return;
}
}
@@ -114,14 +119,14 @@
cursor.getString(PHONE_ACCOUNT_ID));
if (!OmtpVvmSourceManager.getInstance(context)
.isVvmSourceRegistered(mPhoneAccount)) {
- Log.w(TAG, "Account not registered - cannot retrieve message.");
+ VvmLog.w(TAG, "Account not registered - cannot retrieve message.");
return;
}
int subId = PhoneUtils.getSubIdForPhoneAccountHandle(mPhoneAccount);
OmtpVvmCarrierConfigHelper carrierConfigHelper =
new OmtpVvmCarrierConfigHelper(context, subId);
-
+ VvmLog.i(TAG, "Requesting network to fetch voicemail");
mNetworkCallback = new fetchVoicemailNetworkRequestCallback(context,
mPhoneAccount);
mNetworkCallback.requestNetwork();
@@ -153,15 +158,17 @@
public void run() {
try {
while (mRetryCount > 0) {
+ VvmLog.i(TAG, "fetching voicemail, retry count=" + mRetryCount);
ImapHelper imapHelper = new ImapHelper(mContext, mPhoneAccount, network);
if (!imapHelper.isSuccessfullyInitialized()) {
- Log.w(TAG, "Can't retrieve Imap credentials.");
+ VvmLog.w(TAG, "Can't retrieve Imap credentials.");
return;
}
boolean success = imapHelper.fetchVoicemailPayload(
new VoicemailFetchedCallback(mContext, mUri), mUid);
if (!success && mRetryCount > 0) {
+ VvmLog.i(TAG, "fetch voicemail failed, retrying");
mRetryCount--;
} else {
return;
diff --git a/src/com/android/phone/vvm/omtp/fetch/VoicemailFetchedCallback.java b/src/com/android/phone/vvm/omtp/fetch/VoicemailFetchedCallback.java
index 3862d54..387ca5a 100644
--- a/src/com/android/phone/vvm/omtp/fetch/VoicemailFetchedCallback.java
+++ b/src/com/android/phone/vvm/omtp/fetch/VoicemailFetchedCallback.java
@@ -20,8 +20,8 @@
import android.content.Context;
import android.net.Uri;
import android.provider.VoicemailContract.Voicemails;
-import android.util.Log;
+import com.android.phone.vvm.omtp.VvmLog;
import com.android.phone.vvm.omtp.imap.VoicemailPayload;
import libcore.io.IoUtils;
@@ -51,7 +51,7 @@
* @param voicemailPayload The object containing the content data for the voicemail
*/
public void setVoicemailContent(VoicemailPayload voicemailPayload) {
- Log.d(TAG, String.format("Writing new voicemail content: %s", mUri));
+ VvmLog.d(TAG, String.format("Writing new voicemail content: %s", mUri));
OutputStream outputStream = null;
try {
@@ -61,7 +61,7 @@
outputStream.write(inputBytes);
}
} catch (IOException e) {
- Log.w(TAG, String.format("File not found for %s", mUri));
+ VvmLog.w(TAG, String.format("File not found for %s", mUri));
return;
} finally {
IoUtils.closeQuietly(outputStream);
@@ -73,7 +73,8 @@
values.put(Voicemails.HAS_CONTENT, true);
int updatedCount = mContentResolver.update(mUri, values, null, null);
if (updatedCount != 1) {
- Log.e(TAG, "Updating voicemail should have updated 1 row, was: " + updatedCount);
+ VvmLog
+ .e(TAG, "Updating voicemail should have updated 1 row, was: " + updatedCount);
}
}
}
diff --git a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
index ce9e9c3..216b6a4 100644
--- a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
+++ b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
@@ -25,7 +25,6 @@
import android.telecom.PhoneAccountHandle;
import android.telecom.Voicemail;
import android.util.Base64;
-import android.util.Log;
import com.android.phone.PhoneUtils;
import com.android.phone.VoicemailStatus;
@@ -50,6 +49,7 @@
import com.android.phone.vvm.omtp.OmtpConstants.ChangePinResult;
import com.android.phone.vvm.omtp.OmtpEvents;
import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
import com.android.phone.vvm.omtp.fetch.VoicemailFetchedCallback;
import com.android.phone.vvm.omtp.sync.OmtpVvmSyncService.TranscriptionFetchedCallback;
@@ -135,11 +135,11 @@
return mImapStore != null;
}
- public boolean isRoaming(){
+ public boolean isRoaming() {
ConnectivityManager connectivityManager = (ConnectivityManager) mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
NetworkInfo info = connectivityManager.getNetworkInfo(mNetwork);
- if(info == null){
+ if (info == null) {
return false;
}
return info.isRoaming();
@@ -153,12 +153,16 @@
return mImapStore.getConnection();
}
- /** The caller thread will block until the method returns. */
+ /**
+ * The caller thread will block until the method returns.
+ */
public boolean markMessagesAsRead(List<Voicemail> voicemails) {
return setFlags(voicemails, Flag.SEEN);
}
- /** The caller thread will block until the method returns. */
+ /**
+ * The caller thread will block until the method returns.
+ */
public boolean markMessagesAsDeleted(List<Voicemail> voicemails) {
return setFlags(voicemails, Flag.DELETED);
}
@@ -232,7 +236,7 @@
* transcription exists.
*/
private Voicemail getVoicemailFromMessageStructure(
- MessageStructureWrapper messageStructureWrapper) throws MessagingException{
+ MessageStructureWrapper messageStructureWrapper) throws MessagingException {
Message messageDetails = messageStructureWrapper.messageStructure;
TranscriptionFetchedListener listener = new TranscriptionFetchedListener();
@@ -240,7 +244,7 @@
FetchProfile fetchProfile = new FetchProfile();
fetchProfile.add(messageStructureWrapper.transcriptionBodyPart);
- mFolder.fetch(new Message[] {messageDetails}, fetchProfile, listener);
+ mFolder.fetch(new Message[]{messageDetails}, fetchProfile, listener);
}
// Found an audio attachment, this is a valid voicemail.
@@ -257,8 +261,8 @@
}
/**
- * The "from" field of a visual voicemail IMAP message is the number of the caller who left
- * the message. Extract this number from the list of "from" addresses.
+ * The "from" field of a visual voicemail IMAP message is the number of the caller who left the
+ * message. Extract this number from the list of "from" addresses.
*
* @param fromAddresses A list of addresses that comprise the "from" line.
* @return The number of the voicemail sender.
@@ -297,7 +301,7 @@
// The IMAP folder fetch method will call "messageRetrieved" on the listener when the
// message is successfully retrieved.
- mFolder.fetch(new Message[] {message}, fetchProfile, listener);
+ mFolder.fetch(new Message[]{message}, fetchProfile, listener);
return listener.getMessageStructure();
}
@@ -341,7 +345,7 @@
FetchProfile fetchProfile = new FetchProfile();
fetchProfile.add(FetchProfile.Item.BODY);
- mFolder.fetch(new Message[] {message}, fetchProfile, listener);
+ mFolder.fetch(new Message[]{message}, fetchProfile, listener);
return listener.getVoicemailPayload();
}
@@ -367,7 +371,7 @@
// This method is called synchronously so the transcription will be populated
// in the listener once the next method is called.
- mFolder.fetch(new Message[] {message}, fetchProfile, listener);
+ mFolder.fetch(new Message[]{message}, fetchProfile, listener);
callback.setVoicemailTranscription(listener.getVoicemailTranscription());
}
}
@@ -481,7 +485,7 @@
return;
}
if (quota.occupied == mQuotaOccupied && quota.total == mQuotaTotal) {
- Log.v(TAG, "Quota hasn't changed");
+ VvmLog.v(TAG, "Quota hasn't changed");
return;
}
mQuotaOccupied = quota.occupied;
@@ -493,17 +497,20 @@
.putInt(getSharedPrefsKey(PREF_KEY_QUOTA_OCCUPIED), mQuotaOccupied)
.putInt(getSharedPrefsKey(PREF_KEY_QUOTA_TOTAL), mQuotaTotal)
.apply();
- Log.v(TAG, "Quota changed to " + mQuotaOccupied + "/" + mQuotaTotal);
+ VvmLog.v(TAG, "Quota changed to " + mQuotaOccupied + "/" + mQuotaTotal);
}
+
/**
- * A wrapper to hold a message with its header details and the structure for transcriptions
- * (so they can be fetched in the future).
+ * A wrapper to hold a message with its header details and the structure for transcriptions (so
+ * they can be fetched in the future).
*/
public class MessageStructureWrapper {
+
public Message messageStructure;
public BodyPart transcriptionBodyPart;
- public MessageStructureWrapper() { }
+ public MessageStructureWrapper() {
+ }
}
/**
@@ -511,6 +518,7 @@
*/
private final class MessageStructureFetchedListener
implements ImapFolder.MessageRetrievalListener {
+
private MessageStructureWrapper mMessageStructure;
public MessageStructureFetchedListener() {
@@ -542,7 +550,6 @@
* @param message The IMAP message.
* @return The MessageStructureWrapper object corresponding to an IMAP message and
* transcription.
- * @throws MessagingException
*/
private MessageStructureWrapper getMessageOrNull(Message message)
throws MessagingException {
@@ -579,9 +586,12 @@
* Listener for the message body being fetched.
*/
private final class MessageBodyFetchedListener implements ImapFolder.MessageRetrievalListener {
+
private VoicemailPayload mVoicemailPayload;
- /** Returns the fetch voicemail payload. */
+ /**
+ * Returns the fetch voicemail payload.
+ */
public VoicemailPayload getVoicemailPayload() {
return mVoicemailPayload;
}
@@ -602,18 +612,18 @@
private VoicemailPayload getVoicemailPayloadFromMessage(Message message)
throws MessagingException, IOException {
Multipart multipart = (Multipart) message.getBody();
+ List<String> mimeTypes = new ArrayList<>();
for (int i = 0; i < multipart.getCount(); ++i) {
BodyPart bodyPart = multipart.getBodyPart(i);
String bodyPartMimeType = bodyPart.getMimeType().toLowerCase();
- LogUtils.d(TAG, "bodyPart mime type: " + bodyPartMimeType);
-
+ mimeTypes.add(bodyPartMimeType);
if (bodyPartMimeType.startsWith("audio/")) {
byte[] bytes = getDataFromBody(bodyPart.getBody());
LogUtils.d(TAG, String.format("Fetched %s bytes of data", bytes.length));
return new VoicemailPayload(bodyPartMimeType, bytes);
}
}
- LogUtils.e(TAG, "No audio attachment found on this voicemail");
+ LogUtils.e(TAG, "No audio attachment found on this voicemail, mimeTypes:" + mimeTypes);
return null;
}
}
@@ -623,9 +633,12 @@
*/
private final class TranscriptionFetchedListener implements
ImapFolder.MessageRetrievalListener {
+
private String mVoicemailTranscription;
- /** Returns the fetched voicemail transcription. */
+ /**
+ * Returns the fetched voicemail transcription.
+ */
public String getVoicemailTranscription() {
return mVoicemailTranscription;
}
diff --git a/src/com/android/phone/vvm/omtp/protocol/ProtocolHelper.java b/src/com/android/phone/vvm/omtp/protocol/ProtocolHelper.java
index d265bd0..748fd39 100644
--- a/src/com/android/phone/vvm/omtp/protocol/ProtocolHelper.java
+++ b/src/com/android/phone/vvm/omtp/protocol/ProtocolHelper.java
@@ -18,9 +18,9 @@
import android.telephony.SmsManager;
import android.text.TextUtils;
-import android.util.Log;
import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
import com.android.phone.vvm.omtp.sms.OmtpMessageSender;
public class ProtocolHelper {
@@ -33,7 +33,7 @@
int applicationPort = config.getApplicationPort();
String destinationNumber = config.getDestinationNumber();
if (TextUtils.isEmpty(destinationNumber)) {
- Log.w(TAG, "No destination number for this carrier.");
+ VvmLog.w(TAG, "No destination number for this carrier.");
return null;
}
diff --git a/src/com/android/phone/vvm/omtp/protocol/VisualVoicemailProtocol.java b/src/com/android/phone/vvm/omtp/protocol/VisualVoicemailProtocol.java
index 9e56d12..be2a77f 100644
--- a/src/com/android/phone/vvm/omtp/protocol/VisualVoicemailProtocol.java
+++ b/src/com/android/phone/vvm/omtp/protocol/VisualVoicemailProtocol.java
@@ -70,7 +70,8 @@
return command;
}
- public void handleEvent(Context context, int subId, OmtpEvents event) {
- DefaultOmtpEventHandler.handleEvent(context, subId, event);
+ public void handleEvent(Context context, OmtpVvmCarrierConfigHelper config,
+ OmtpEvents event) {
+ DefaultOmtpEventHandler.handleEvent(context, config, event);
}
}
diff --git a/src/com/android/phone/vvm/omtp/protocol/VisualVoicemailProtocolFactory.java b/src/com/android/phone/vvm/omtp/protocol/VisualVoicemailProtocolFactory.java
index dbf38c2..5f54a50 100644
--- a/src/com/android/phone/vvm/omtp/protocol/VisualVoicemailProtocolFactory.java
+++ b/src/com/android/phone/vvm/omtp/protocol/VisualVoicemailProtocolFactory.java
@@ -18,7 +18,8 @@
import android.annotation.Nullable;
import android.telephony.TelephonyManager;
-import android.util.Log;
+
+import com.android.phone.vvm.omtp.VvmLog;
public class VisualVoicemailProtocolFactory {
@@ -39,7 +40,7 @@
case VVM_TYPE_VVM3:
return new Vvm3Protocol();
default:
- Log.e(TAG, "Unexpected visual voicemail type: " + type);
+ VvmLog.e(TAG, "Unexpected visual voicemail type: " + type);
}
return null;
}
diff --git a/src/com/android/phone/vvm/omtp/protocol/Vvm3EventHandler.java b/src/com/android/phone/vvm/omtp/protocol/Vvm3EventHandler.java
new file mode 100644
index 0000000..8eacb99
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/protocol/Vvm3EventHandler.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2016 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.vvm.omtp.protocol;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.telecom.PhoneAccountHandle;
+import android.util.Log;
+
+import com.android.phone.VoicemailStatus;
+import com.android.phone.settings.VoicemailChangePinDialogPreference;
+import com.android.phone.vvm.omtp.DefaultOmtpEventHandler;
+import com.android.phone.vvm.omtp.OmtpEvents;
+import com.android.phone.vvm.omtp.OmtpEvents.Type;
+import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.utils.PhoneAccountHandleConverter;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Handles {@link OmtpEvents} when {@link Vvm3Protocol} is being used. This handler writes custom
+ * error codes into the voicemail status table so support on the dialer side is required.
+ *
+ * TODO(b/29577838) disable VVM3 by default so support on system dialer can be ensured.
+ */
+public class Vvm3EventHandler {
+
+ private static final String TAG = "Vvm3EventHandler";
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({VMS_DNS_FAILURE, VMG_DNS_FAILURE, SPG_DNS_FAILURE, VMS_NO_CELLULAR, VMG_NO_CELLULAR,
+ SPG_NO_CELLULAR, VMS_TIMEOUT, VMG_TIMEOUT, STATUS_SMS_TIMEOUT, SUBSCRIBER_BLOCKED,
+ UNKNOWN_USER, UNKNOWN_DEVICE, INVALID_PASSWORD, MAILBOX_NOT_INITIALIZED,
+ SERVICE_NOT_PROVISIONED, SERVICE_NOT_ACTIVATED, USER_BLOCKED, IMAP_GETQUOTA_ERROR,
+ IMAP_SELECT_ERROR, IMAP_ERROR, VMG_INTERNAL_ERROR, VMG_DB_ERROR,
+ VMG_COMMUNICATION_ERROR, SPG_URL_NOT_FOUND, VMG_UNKNOWN_ERROR, PIN_NOT_SET})
+ public @interface ErrorCode {
+
+ }
+
+ public static final int VMS_DNS_FAILURE = -9001;
+ public static final int VMG_DNS_FAILURE = -9002;
+ public static final int SPG_DNS_FAILURE = -9003;
+ public static final int VMS_NO_CELLULAR = -9004;
+ public static final int VMG_NO_CELLULAR = -9005;
+ public static final int SPG_NO_CELLULAR = -9006;
+ public static final int VMS_TIMEOUT = -9007;
+ public static final int VMG_TIMEOUT = -9008;
+ public static final int STATUS_SMS_TIMEOUT = -9009;
+
+ public static final int SUBSCRIBER_BLOCKED = -9990;
+ public static final int UNKNOWN_USER = -9991;
+ public static final int UNKNOWN_DEVICE = -9992;
+ public static final int INVALID_PASSWORD = -9993;
+ public static final int MAILBOX_NOT_INITIALIZED = -9994;
+ public static final int SERVICE_NOT_PROVISIONED = -9995;
+ public static final int SERVICE_NOT_ACTIVATED = -9996;
+ public static final int USER_BLOCKED = -9998;
+ public static final int IMAP_GETQUOTA_ERROR = -9997;
+ public static final int IMAP_SELECT_ERROR = -9989;
+ public static final int IMAP_ERROR = -9999;
+
+ public static final int VMG_INTERNAL_ERROR = -101;
+ public static final int VMG_DB_ERROR = -102;
+ public static final int VMG_COMMUNICATION_ERROR = -103;
+ public static final int SPG_URL_NOT_FOUND = -301;
+
+ // Non VVM3 codes:
+ public static final int VMG_UNKNOWN_ERROR = -1;
+ public static final int PIN_NOT_SET = -100;
+
+
+ public static void handleEvent(Context context, OmtpVvmCarrierConfigHelper config,
+ OmtpEvents event) {
+ boolean handled = false;
+ switch (event.getType()) {
+ case Type.CONFIGURATION:
+ handled = handleConfigurationEvent(context, config, event);
+ break;
+ case Type.DATA_CHANNEL:
+ handled = handleDataChannelEvent(context, config, event);
+ break;
+ case Type.NOTIFICATION_CHANNEL:
+ handled = handleNotificationChannelEvent(context, config, event);
+ break;
+ case Type.OTHER:
+ handled = handleOtherEvent(context, config, event);
+ break;
+ default:
+ com.android.services.telephony.Log
+ .wtf(TAG, "invalid event type " + event.getType() + " for " + event);
+ }
+ if (!handled) {
+ DefaultOmtpEventHandler.handleEvent(context, config, event);
+ }
+ }
+
+ private static boolean handleConfigurationEvent(Context context,
+ OmtpVvmCarrierConfigHelper config, OmtpEvents event) {
+ switch (event) {
+ case CONFIG_REQUEST_STATUS_SUCCESS:
+ PhoneAccountHandle handle = PhoneAccountHandleConverter
+ .fromSubId(config.getSubId());
+ if (VoicemailChangePinDialogPreference.getDefaultOldPin(context, handle) == null) {
+ return false;
+ } else {
+ postError(context, config, PIN_NOT_SET);
+ }
+ break;
+ case CONFIG_DEFAULT_PIN_REPLACED:
+ postError(context, config, PIN_NOT_SET);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean handleDataChannelEvent(Context context,
+ OmtpVvmCarrierConfigHelper config, OmtpEvents event) {
+ switch (event) {
+ case DATA_NO_CONNECTION:
+ case DATA_NO_CONNECTION_CELLULAR_REQUIRED:
+ postError(context, config, VMS_NO_CELLULAR);
+ break;
+ case DATA_CANNOT_RESOLVE_HOST_ON_NETWORK:
+ postError(context, config, VMS_DNS_FAILURE);
+ break;
+ case DATA_BAD_IMAP_CREDENTIAL:
+ postError(context, config, IMAP_ERROR);
+ break;
+ case DATA_AUTH_UNKNOWN_USER:
+ postError(context, config, UNKNOWN_USER);
+ break;
+ case DATA_AUTH_UNKNOWN_DEVICE:
+ postError(context, config, UNKNOWN_DEVICE);
+ break;
+ case DATA_AUTH_INVALID_PASSWORD:
+ postError(context, config, INVALID_PASSWORD);
+ break;
+ case DATA_AUTH_MAILBOX_NOT_INITIALIZED:
+ postError(context, config, MAILBOX_NOT_INITIALIZED);
+ break;
+ case DATA_AUTH_SERVICE_NOT_PROVISIONED:
+ postError(context, config, SERVICE_NOT_PROVISIONED);
+ break;
+ case DATA_AUTH_SERVICE_NOT_ACTIVATED:
+ postError(context, config, SERVICE_NOT_ACTIVATED);
+ break;
+ case DATA_AUTH_USER_IS_BLOCKED:
+ postError(context, config, USER_BLOCKED);
+ break;
+
+ case DATA_INVALID_PORT:
+ case DATA_SSL_INVALID_HOST_NAME:
+ case DATA_CANNOT_ESTABLISH_SSL_SESSION:
+ case DATA_IOE_ON_OPEN:
+ case DATA_REJECTED_SERVER_RESPONSE:
+ case DATA_INVALID_INITIAL_SERVER_RESPONSE:
+ case DATA_SSL_EXCEPTION:
+ case DATA_ALL_SOCKET_CONNECTION_FAILED:
+ postError(context, config, IMAP_ERROR);
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean handleNotificationChannelEvent(Context context,
+ OmtpVvmCarrierConfigHelper config, OmtpEvents event) {
+ return false;
+ }
+
+ private static boolean handleOtherEvent(Context context, OmtpVvmCarrierConfigHelper config,
+ OmtpEvents event) {
+ switch (event) {
+ case VVM3_NEW_USER_SETUP_FAILED:
+ postError(context, config, MAILBOX_NOT_INITIALIZED);
+ break;
+ case VVM3_VMG_DNS_FAILURE:
+ postError(context, config, VMG_DNS_FAILURE);
+ break;
+ case VVM3_SPG_DNS_FAILURE:
+ postError(context, config, SPG_DNS_FAILURE);
+ break;
+ case VVM3_VMG_CONNECTION_FAILED:
+ postError(context, config, VMG_NO_CELLULAR);
+ break;
+ case VVM3_SPG_CONNECTION_FAILED:
+ postError(context, config, SPG_NO_CELLULAR);
+ break;
+ case VVM3_VMG_TIMEOUT:
+ postError(context, config, VMG_TIMEOUT);
+ break;
+
+ case VVM3_SUBSCRIBER_PROVISIONED:
+ postError(context, config, SERVICE_NOT_ACTIVATED);
+ case VVM3_SUBSCRIBER_BLOCKED:
+ postError(context, config, SUBSCRIBER_BLOCKED);
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ private static void postError(Context context, OmtpVvmCarrierConfigHelper config,
+ @ErrorCode int errorCode) {
+ VoicemailStatus.Editor editor = VoicemailStatus.edit(context, config.getSubId());
+
+ switch (errorCode) {
+ case VMG_DNS_FAILURE:
+ case SPG_DNS_FAILURE:
+ case VMG_NO_CELLULAR:
+ case SPG_NO_CELLULAR:
+ case VMG_TIMEOUT:
+ case SUBSCRIBER_BLOCKED:
+ case UNKNOWN_USER:
+ case UNKNOWN_DEVICE:
+ case INVALID_PASSWORD:
+ case MAILBOX_NOT_INITIALIZED:
+ case SERVICE_NOT_PROVISIONED:
+ case SERVICE_NOT_ACTIVATED:
+ case USER_BLOCKED:
+ case VMG_UNKNOWN_ERROR:
+ case SPG_URL_NOT_FOUND:
+ case VMG_INTERNAL_ERROR:
+ case VMG_DB_ERROR:
+ case VMG_COMMUNICATION_ERROR:
+ case PIN_NOT_SET:
+ editor.setConfigurationState(errorCode);
+ break;
+ case VMS_NO_CELLULAR:
+ case VMS_DNS_FAILURE:
+ case VMS_TIMEOUT:
+ case IMAP_GETQUOTA_ERROR:
+ case IMAP_SELECT_ERROR:
+ case IMAP_ERROR:
+ editor.setDataChannelState(errorCode);
+ break;
+ case STATUS_SMS_TIMEOUT:
+ editor.setNotificationChannelState(errorCode);
+ break;
+ default:
+ Log.wtf(TAG, "unknown error code: " + errorCode);
+ }
+ editor.apply();
+ }
+}
diff --git a/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java b/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java
index e62d1cf..306006d 100644
--- a/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java
+++ b/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java
@@ -17,17 +17,19 @@
package com.android.phone.vvm.omtp.protocol;
import android.annotation.Nullable;
+import android.content.Context;
import android.net.Network;
import android.os.Bundle;
import android.telecom.PhoneAccountHandle;
import android.telephony.SmsManager;
-import android.util.Log;
import com.android.phone.common.mail.MessagingException;
import com.android.phone.settings.VisualVoicemailSettingsUtil;
import com.android.phone.settings.VoicemailChangePinDialogPreference;
import com.android.phone.vvm.omtp.OmtpConstants;
+import com.android.phone.vvm.omtp.OmtpEvents;
import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
import com.android.phone.vvm.omtp.imap.ImapHelper;
import com.android.phone.vvm.omtp.sms.OmtpMessageSender;
import com.android.phone.vvm.omtp.sms.StatusMessage;
@@ -53,20 +55,20 @@
private static String ISO639_Spanish = "es";
- private static String VVM3_VM_LANGUAGE_ENGLISH_STANDARD = "1";
- private static String VVM3_VM_LANGUAGE_SPANISH_STANDARD = "2";
+ // Default prompt level when using the telephone user interface.
+ // Standard prompt when the user call into the voicemail, and no prompts when someone else is
+ // leaving a voicemail.
+ private static String VVM3_VM_LANGUAGE_ENGLISH_STANDARD_NO_GUEST_PROMPTS = "5";
+ private static String VVM3_VM_LANGUAGE_SPANISH_STANDARD_NO_GUEST_PROMPTS = "6";
private static final int PIN_LENGTH = 6;
- public Vvm3Protocol() {
- Log.d(TAG, "Vvm3Protocol created");
- }
-
@Override
public void startActivation(OmtpVvmCarrierConfigHelper config) {
// VVM3 does not support activation SMS.
// Send a status request which will start the provisioning process if the user is not
// provisioned.
+ VvmLog.i(TAG, "Activating");
config.requestStatus();
}
@@ -79,15 +81,22 @@
@Override
public void startProvisioning(PhoneAccountHandle phoneAccountHandle,
OmtpVvmCarrierConfigHelper config, StatusMessage message, Bundle data) {
- Log.i(TAG, "start vvm3 provisioning");
- if ("U".equals(message.getProvisioningStatus())) {
- Log.i(TAG, "Provisioning status: Unknown, subscribing");
+ VvmLog.i(TAG, "start vvm3 provisioning");
+ if (OmtpConstants.SUBSCRIBER_UNKNOWN.equals(message.getProvisioningStatus())) {
+ VvmLog.i(TAG, "Provisioning status: Unknown, subscribing");
new Vvm3Subscriber(phoneAccountHandle, config, data).subscribe();
- } else if ("N".equals(message.getProvisioningStatus())) {
- Log.i(TAG, "setting up new user");
+ } else if (OmtpConstants.SUBSCRIBER_NEW.equals(message.getProvisioningStatus())) {
+ VvmLog.i(TAG, "setting up new user");
VisualVoicemailSettingsUtil.setVisualVoicemailCredentialsFromStatusMessage(
config.getContext(), phoneAccountHandle, message);
startProvisionNewUser(phoneAccountHandle, config, message);
+ } else if (OmtpConstants.SUBSCRIBER_PROVISIONED.equals(message.getProvisioningStatus())) {
+ VvmLog.i(TAG, "User provisioned but not activated, disabling VVM");
+ VisualVoicemailSettingsUtil
+ .setVisualVoicemailEnabled(config.getContext(), phoneAccountHandle, false);
+ } else if (OmtpConstants.SUBSCRIBER_BLOCKED.equals(message.getProvisioningStatus())) {
+ VvmLog.i(TAG, "User blocked");
+ config.handleEvent(OmtpEvents.VVM3_SUBSCRIBER_BLOCKED);
}
}
@@ -98,6 +107,11 @@
}
@Override
+ public void handleEvent(Context context, OmtpVvmCarrierConfigHelper config, OmtpEvents event) {
+ Vvm3EventHandler.handleEvent(context, config, event);
+ }
+
+ @Override
public String getCommand(String command) {
if (command == OmtpConstants.IMAP_CHANGE_TUI_PWD_FORMAT) {
return IMAP_CHANGE_TUI_PWD_FORMAT;
@@ -132,7 +146,7 @@
@Override
public void onAvailable(Network network) {
super.onAvailable(network);
- Log.i(TAG, "new user: network available");
+ VvmLog.i(TAG, "new user: network available");
ImapHelper helper = new ImapHelper(mContext, mPhoneAccount, network);
try {
@@ -143,45 +157,48 @@
if (Locale.getDefault().getLanguage()
.equals(new Locale(ISO639_Spanish).getLanguage())) {
// Spanish
- helper.changeVoicemailTuiLanguage(VVM3_VM_LANGUAGE_SPANISH_STANDARD);
+ helper.changeVoicemailTuiLanguage(
+ VVM3_VM_LANGUAGE_SPANISH_STANDARD_NO_GUEST_PROMPTS);
} else {
// English
- helper.changeVoicemailTuiLanguage(VVM3_VM_LANGUAGE_ENGLISH_STANDARD);
+ helper.changeVoicemailTuiLanguage(
+ VVM3_VM_LANGUAGE_ENGLISH_STANDARD_NO_GUEST_PROMPTS);
}
- Log.i(TAG, "new user: language set");
+ VvmLog.i(TAG, "new user: language set");
if (setPin(helper)) {
// Only close new user tutorial if the PIN has been changed.
helper.closeNewUserTutorial();
- Log.i(TAG, "new user: NUT closed");
+ VvmLog.i(TAG, "new user: NUT closed");
mConfig.requestStatus();
}
} catch (MessagingException | IOException e) {
- Log.e(TAG, e.toString());
+ helper.handleEvent(OmtpEvents.VVM3_NEW_USER_SETUP_FAILED);
+ VvmLog.e(TAG, e.toString());
}
}
private boolean setPin(ImapHelper helper) throws IOException, MessagingException {
String defaultPin = getDefaultPin();
if (defaultPin == null) {
+ VvmLog.i(TAG, "cannot generate default PIN");
return false;
}
if (VoicemailChangePinDialogPreference.getDefaultOldPin(mContext, mPhoneAccount)
!= null) {
// The pin was already set
+ VvmLog.i(TAG, "PIN already set");
return true;
}
String newPin = generatePin();
if (helper.changePin(defaultPin, newPin) == OmtpConstants.CHANGE_PIN_SUCCESS) {
VoicemailChangePinDialogPreference
.setDefaultOldPIN(mContext, mPhoneAccount, newPin);
-
- // TODO(b/29082418): set CONFIGURATION_STATE to VVM3_CONFIGURATION_PIN_NOT_SET
- // to prompt the user to set the PIN
+ helper.handleEvent(OmtpEvents.CONFIG_DEFAULT_PIN_REPLACED);
}
- Log.i(TAG, "new user: PIN set");
+ VvmLog.i(TAG, "new user: PIN set");
return true;
}
@@ -192,12 +209,12 @@
try {
String number = username.substring(0, username.indexOf('@'));
if (number.length() < 4) {
- Log.e(TAG, "unable to extract number from IMAP username");
+ VvmLog.e(TAG, "unable to extract number from IMAP username");
return null;
}
return "1" + number.substring(number.length() - 4);
} catch (StringIndexOutOfBoundsException e) {
- Log.e(TAG, "unable to extract number from IMAP username");
+ VvmLog.e(TAG, "unable to extract number from IMAP username");
return null;
}
diff --git a/src/com/android/phone/vvm/omtp/protocol/Vvm3Subscriber.java b/src/com/android/phone/vvm/omtp/protocol/Vvm3Subscriber.java
index c314ff5..980701e 100644
--- a/src/com/android/phone/vvm/omtp/protocol/Vvm3Subscriber.java
+++ b/src/com/android/phone/vvm/omtp/protocol/Vvm3Subscriber.java
@@ -26,10 +26,11 @@
import android.text.Spanned;
import android.text.style.URLSpan;
import android.util.ArrayMap;
-import android.util.Log;
import com.android.phone.Assert;
+import com.android.phone.vvm.omtp.OmtpEvents;
import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
import com.android.phone.vvm.omtp.sync.VvmNetworkRequestCallback;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
@@ -151,6 +152,7 @@
Assert.isNotMainThread();
// Cellular data is required to subscribe.
// processSubscription() is called after network is available.
+ VvmLog.i(TAG, "Subscribing");
new Vvm3ProvisioningNetworkRequestCallback(mHelper, mHandle).requestNetwork();
}
@@ -161,7 +163,7 @@
String subscribeLink = findSubscribeLink(selfProvisionResponse);
clickSubscribeLink(subscribeLink);
} catch (ProvisioningException e) {
- Log.e(TAG, e.toString());
+ VvmLog.e(TAG, e.toString());
}
}
@@ -169,6 +171,7 @@
* Get the URL to perform self-provisioning from the voicemail management gateway.
*/
private String getSelfProvisioningGateway() throws ProvisioningException {
+ VvmLog.i(TAG, "retrieving SPG URL");
String response = vvm3XmlRequest(OPERATION_GET_SPG_URL);
return extractText(response, SPG_URL_TAG);
}
@@ -179,6 +182,8 @@
* subscription. The cookie from this response and cellular data is required to click the link.
*/
private String getSelfProvisionResponse(String url) throws ProvisioningException {
+ VvmLog.i(TAG, "Retrieving self provisioning response");
+
RequestFuture<String> future = RequestFuture.newFuture();
StringRequest stringRequest = new StringRequest(Request.Method.POST, url, future, future) {
@@ -200,11 +205,13 @@
try {
return future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
+ mHelper.handleEvent(OmtpEvents.VVM3_SPG_CONNECTION_FAILED);
throw new ProvisioningException(e.toString());
}
}
private void clickSubscribeLink(String subscribeLink) throws ProvisioningException {
+ VvmLog.i(TAG, "Clicking subscribe link");
RequestFuture<String> future = RequestFuture.newFuture();
StringRequest stringRequest = new StringRequest(Request.Method.POST,
@@ -214,15 +221,16 @@
try {
future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
+ mHelper.handleEvent(OmtpEvents.VVM3_SPG_CONNECTION_FAILED);
throw new ProvisioningException(e.toString());
}
}
private String vvm3XmlRequest(String operation) throws ProvisioningException {
- Log.d(TAG, "Sending vvm3XmlRequest for " + operation);
+ VvmLog.d(TAG, "Sending vvm3XmlRequest for " + operation);
String voicemailManagementGateway = mData.getString(VMG_URL_KEY);
if (voicemailManagementGateway == null) {
- Log.e(TAG, "voicemailManagementGateway url unknown");
+ VvmLog.e(TAG, "voicemailManagementGateway url unknown");
return null;
}
String transactionId = createTransactionId();
@@ -246,6 +254,7 @@
}
return response;
} catch (InterruptedException | ExecutionException | TimeoutException e) {
+ mHelper.handleEvent(OmtpEvents.VVM3_VMG_CONNECTION_FAILED);
throw new ProvisioningException(e.toString());
}
}
@@ -285,7 +294,7 @@
@Override
public void onAvailable(Network network) {
super.onAvailable(network);
- Log.d(TAG, "provisioning: network available");
+ VvmLog.d(TAG, "provisioning: network available");
mRequestQueue = Volley
.newRequestQueue(mContext, new NetworkSpecifiedHurlStack(network));
processSubscription();
diff --git a/src/com/android/phone/vvm/omtp/sms/LegacyModeSmsHandler.java b/src/com/android/phone/vvm/omtp/sms/LegacyModeSmsHandler.java
new file mode 100644
index 0000000..ba5bd70
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/sms/LegacyModeSmsHandler.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 Google Inc. All Rights Reserved.
+ *
+ * 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.vvm.omtp.sms;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.VoicemailContract;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.internal.telephony.Phone;
+import com.android.phone.PhoneUtils;
+import com.android.phone.vvm.omtp.OmtpConstants;
+import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
+
+/**
+ * Class ot handle voicemail SMS under legacy mode
+ *
+ * @see OmtpVvmCarrierConfigHelper#isLegacyModeEnabled()
+ */
+public class LegacyModeSmsHandler {
+
+ private static final String TAG = "LegacyModeSmsHandler";
+
+ public static void handle(Context context, Intent intent, PhoneAccountHandle handle) {
+ VvmLog.v(TAG, "processing VVM SMS on legacy mode");
+ String eventType = intent.getExtras()
+ .getString(VoicemailContract.EXTRA_VOICEMAIL_SMS_PREFIX);
+ Bundle data = intent.getExtras().getBundle(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS);
+
+ if (eventType.equals(OmtpConstants.SYNC_SMS_PREFIX)) {
+ SyncMessage message = new SyncMessage(data);
+ VvmLog.v(TAG, "Received SYNC sms for " + handle.getId() +
+ " with event " + message.getSyncTriggerEvent());
+
+ switch (message.getSyncTriggerEvent()) {
+ case OmtpConstants.NEW_MESSAGE:
+ case OmtpConstants.MAILBOX_UPDATE:
+ // The user has called into the voicemail and the new message count could
+ // change.
+ // For some carriers new message count could be set to 0 even if there are still
+ // unread messages, to clear the message waiting indicator.
+ VvmLog.v(TAG, "updating MWI");
+ Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(handle);
+ // Setting voicemail message count to non-zero will show the telephony voicemail
+ // notification, and zero will clear it.
+ phone.setVoiceMessageCount(message.getNewMessageCount());
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
index feb3c5a..c930a98 100644
--- a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
+++ b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
@@ -25,14 +25,13 @@
import android.provider.VoicemailContract;
import android.telecom.PhoneAccountHandle;
import android.telecom.Voicemail;
-import android.util.Log;
import com.android.phone.PhoneGlobals;
import com.android.phone.settings.VisualVoicemailSettingsUtil;
-import com.android.phone.vvm.omtp.LocalLogHelper;
import com.android.phone.vvm.omtp.OmtpConstants;
import com.android.phone.vvm.omtp.OmtpEvents;
import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
import com.android.phone.vvm.omtp.sync.OmtpVvmSourceManager;
import com.android.phone.vvm.omtp.sync.OmtpVvmSyncService;
import com.android.phone.vvm.omtp.sync.VoicemailsQueryHelper;
@@ -49,7 +48,7 @@
@Override
public void onReceive(Context context, Intent intent) {
if (!UserManager.get(context).isUserUnlocked()) {
- Log.i(TAG, "Received message on locked device");
+ VvmLog.i(TAG, "Received message on locked device");
// A full sync will happen after the device is unlocked, so nothing need to be done.
return;
}
@@ -59,12 +58,17 @@
PhoneAccountHandle phone = PhoneAccountHandleConverter.fromSubId(subId);
if (phone == null) {
- Log.i(TAG, "Received message for null phone account");
+ VvmLog.i(TAG, "Received message for null phone account");
return;
}
+ OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(mContext, subId);
if (!VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(mContext, phone)) {
- Log.i(TAG, "Received vvm message for disabled vvm source.");
+ if (helper.isLegacyModeEnabled()) {
+ LegacyModeSmsHandler.handle(context, intent, phone);
+ } else {
+ VvmLog.i(TAG, "Received vvm message for disabled vvm source.");
+ }
return;
}
@@ -75,23 +79,20 @@
if (eventType.equals(OmtpConstants.SYNC_SMS_PREFIX)) {
SyncMessage message = new SyncMessage(data);
- Log.v(TAG, "Received SYNC sms for " + phone.getId() +
- " with event " + message.getSyncTriggerEvent());
- LocalLogHelper.log(TAG, "Received SYNC sms for " + phone.getId() +
+ VvmLog.v(TAG, "Received SYNC sms for " + subId +
" with event " + message.getSyncTriggerEvent());
processSync(phone, message);
} else if (eventType.equals(OmtpConstants.STATUS_SMS_PREFIX)) {
- Log.v(TAG, "Received STATUS sms for " + phone.getId());
- LocalLogHelper.log(TAG, "Received Status sms for " + phone.getId());
+ VvmLog.v(TAG, "Received Status sms for " + subId);
StatusMessage message = new StatusMessage(data);
if (message.getProvisioningStatus().equals(OmtpConstants.SUBSCRIBER_READY)) {
updateSource(phone, subId, message);
} else {
- Log.v(TAG, "Subscriber not ready, start provisioning");
+ VvmLog.v(TAG, "Subscriber not ready, start provisioning");
mContext.startService(OmtpProvisioningService.getProvisionIntent(mContext, intent));
}
} else {
- Log.e(TAG, "Unknown prefix: " + eventType);
+ VvmLog.e(TAG, "Unknown prefix: " + eventType);
}
}
@@ -133,7 +134,8 @@
// Not implemented in V1
break;
default:
- Log.e(TAG, "Unrecognized sync trigger event: " + message.getSyncTriggerEvent());
+ VvmLog.e(TAG,
+ "Unrecognized sync trigger event: " + message.getSyncTriggerEvent());
break;
}
@@ -166,11 +168,7 @@
PhoneGlobals.getInstance().clearMwiIndicator(subId);
} else {
- Log.w(TAG, "Visual voicemail not available for subscriber.");
- // Override default isEnabled setting to false since visual voicemail is unable to
- // be accessed for some reason.
- VisualVoicemailSettingsUtil.setVisualVoicemailEnabled(mContext, phone,
- /* isEnabled */ false, /* isUserSet */ true);
+ VvmLog.e(TAG, "Visual voicemail not available for subscriber.");
}
}
}
diff --git a/src/com/android/phone/vvm/omtp/sms/OmtpMessageSender.java b/src/com/android/phone/vvm/omtp/sms/OmtpMessageSender.java
index 9080292..9a775f0 100644
--- a/src/com/android/phone/vvm/omtp/sms/OmtpMessageSender.java
+++ b/src/com/android/phone/vvm/omtp/sms/OmtpMessageSender.java
@@ -20,9 +20,10 @@
import android.telephony.SmsManager;
import com.android.phone.vvm.omtp.OmtpConstants;
-import com.android.services.telephony.Log;
+import com.android.phone.vvm.omtp.VvmLog;
import java.io.UnsupportedEncodingException;
+import java.util.Locale;
/**
* Send client originated OMTP messages to the OMTP server.
@@ -73,9 +74,10 @@
protected void sendSms(String text, PendingIntent sentIntent) {
// If application port is set to 0 then send simple text message, else send data message.
if (mApplicationPort == 0) {
- Log.v(TAG, String.format("Sending TEXT sms '%s' to %s", text, mDestinationNumber));
+ VvmLog
+ .v(TAG, String.format("Sending TEXT sms '%s' to %s", text, mDestinationNumber));
mSmsManager.sendTextMessageWithSelfPermissions(mDestinationNumber, null, text,
- sentIntent, null);
+ sentIntent, null, false);
} else {
byte[] data;
try {
@@ -83,8 +85,9 @@
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("Failed to encode: " + text);
}
- Log.v(TAG, String.format("Sending BINARY sms '%s' to %s:%d", text, mDestinationNumber,
- mApplicationPort));
+ VvmLog.v(TAG,
+ String.format(Locale.US, "Sending BINARY sms '%s' to %s:%d", text,
+ mDestinationNumber, mApplicationPort));
mSmsManager.sendDataMessageWithSelfPermissions(mDestinationNumber, null,
mApplicationPort, data, sentIntent, null);
}
diff --git a/src/com/android/phone/vvm/omtp/sms/StatusMessage.java b/src/com/android/phone/vvm/omtp/sms/StatusMessage.java
index ee1f07d..f9d972f 100644
--- a/src/com/android/phone/vvm/omtp/sms/StatusMessage.java
+++ b/src/com/android/phone/vvm/omtp/sms/StatusMessage.java
@@ -18,6 +18,7 @@
import android.os.Bundle;
import android.telecom.Log;
+import com.android.phone.NeededForTesting;
import com.android.phone.vvm.omtp.OmtpConstants;
/**
@@ -61,19 +62,19 @@
}
public StatusMessage(Bundle wrappedData) {
- mProvisioningStatus = unquote(wrappedData.getString(OmtpConstants.PROVISIONING_STATUS));
- mStatusReturnCode = wrappedData.getString(OmtpConstants.RETURN_CODE);
- mSubscriptionUrl = wrappedData.getString(OmtpConstants.SUBSCRIPTION_URL);
- mServerAddress = wrappedData.getString(OmtpConstants.SERVER_ADDRESS);
- mTuiAccessNumber = wrappedData.getString(OmtpConstants.TUI_ACCESS_NUMBER);
- mClientSmsDestinationNumber = wrappedData.getString(
+ mProvisioningStatus = unquote(getString(wrappedData, OmtpConstants.PROVISIONING_STATUS));
+ mStatusReturnCode = getString(wrappedData, OmtpConstants.RETURN_CODE);
+ mSubscriptionUrl = getString(wrappedData, OmtpConstants.SUBSCRIPTION_URL);
+ mServerAddress = getString(wrappedData, OmtpConstants.SERVER_ADDRESS);
+ mTuiAccessNumber = getString(wrappedData, OmtpConstants.TUI_ACCESS_NUMBER);
+ mClientSmsDestinationNumber = getString(wrappedData,
OmtpConstants.CLIENT_SMS_DESTINATION_NUMBER);
- mImapPort = wrappedData.getString(OmtpConstants.IMAP_PORT);
- mImapUserName = wrappedData.getString(OmtpConstants.IMAP_USER_NAME);
- mImapPassword = wrappedData.getString(OmtpConstants.IMAP_PASSWORD);
- mSmtpPort = wrappedData.getString(OmtpConstants.SMTP_PORT);
- mSmtpUserName = wrappedData.getString(OmtpConstants.SMTP_USER_NAME);
- mSmtpPassword = wrappedData.getString(OmtpConstants.SMTP_PASSWORD);
+ mImapPort = getString(wrappedData, OmtpConstants.IMAP_PORT);
+ mImapUserName = getString(wrappedData, OmtpConstants.IMAP_USER_NAME);
+ mImapPassword = getString(wrappedData, OmtpConstants.IMAP_PASSWORD);
+ mSmtpPort = getString(wrappedData, OmtpConstants.SMTP_PORT);
+ mSmtpUserName = getString(wrappedData, OmtpConstants.SMTP_USER_NAME);
+ mSmtpPassword = getString(wrappedData, OmtpConstants.SMTP_PASSWORD);
}
private static String unquote(String string) {
@@ -104,6 +105,7 @@
* @return the URL of the voicemail server. This is the URL to send the users to for subscribing
* to the visual voicemail service.
*/
+ @NeededForTesting
public String getSubscriptionUrl() {
return mSubscriptionUrl;
}
@@ -120,6 +122,7 @@
* @return the Telephony User Interface number to call to access voicemails directly from the
* IVR.
*/
+ @NeededForTesting
public String getTuiAccessNumber() {
return mTuiAccessNumber;
}
@@ -127,6 +130,7 @@
/**
* @return the number to which client originated SMSes should be sent to.
*/
+ @NeededForTesting
public String getClientSmsDestinationNumber() {
return mClientSmsDestinationNumber;
}
@@ -155,6 +159,7 @@
/**
* @return the SMTP server port to talk to.
*/
+ @NeededForTesting
public String getSmtpPort() {
return mSmtpPort;
}
@@ -162,6 +167,7 @@
/**
* @return the SMTP user name to be used for SMTP authentication.
*/
+ @NeededForTesting
public String getSmtpUserName() {
return mSmtpUserName;
}
@@ -169,7 +175,16 @@
/**
* @return the SMTP password to be used for SMTP authentication.
*/
+ @NeededForTesting
public String getSmtpPassword() {
return mSmtpPassword;
}
+
+ private static String getString(Bundle bundle, String key) {
+ String value = bundle.getString(key);
+ if (value == null) {
+ return "";
+ }
+ return value;
+ }
}
\ No newline at end of file
diff --git a/src/com/android/phone/vvm/omtp/sms/SyncMessage.java b/src/com/android/phone/vvm/omtp/sms/SyncMessage.java
index 4dddba4..632ff9e 100644
--- a/src/com/android/phone/vvm/omtp/sms/SyncMessage.java
+++ b/src/com/android/phone/vvm/omtp/sms/SyncMessage.java
@@ -18,6 +18,7 @@
import android.annotation.Nullable;
import android.os.Bundle;
+import com.android.phone.NeededForTesting;
import com.android.phone.vvm.omtp.OmtpConstants;
import java.text.ParseException;
@@ -33,18 +34,17 @@
// Sync event that triggered this message.
private final String mSyncTriggerEvent;
// Total number of new messages on the server.
- private final Integer mNewMessageCount;
+ private final int mNewMessageCount;
// UID of the new message.
private final String mMessageId;
// Length of the message.
- @Nullable
- private final Integer mMessageLength;
+ private final int mMessageLength;
// Content type (voice, video, fax...) of the new message.
private final String mContentType;
// Sender of the new message.
private final String mSender;
// Timestamp (in millis) of the new message.
- private final Long mMsgTimeMillis;
+ private final long mMsgTimeMillis;
@Override
public String toString() {
@@ -58,21 +58,19 @@
}
public SyncMessage(Bundle wrappedData) {
- mSyncTriggerEvent = wrappedData.getString(OmtpConstants.SYNC_TRIGGER_EVENT);
- mMessageId = wrappedData.getString(OmtpConstants.MESSAGE_UID);
- if (wrappedData.getString(OmtpConstants.MESSAGE_LENGTH) != null) {
- mMessageLength = Integer.parseInt(wrappedData.getString(OmtpConstants.MESSAGE_LENGTH));
- } else {
- // Optional field
- mMessageLength = null;
- }
- mContentType = wrappedData.getString(OmtpConstants.CONTENT_TYPE);
- mSender = wrappedData.getString(OmtpConstants.SENDER);
- mNewMessageCount = Integer.parseInt(wrappedData.getString(OmtpConstants.NUM_MESSAGE_COUNT));
+ mSyncTriggerEvent = getString(wrappedData, OmtpConstants.SYNC_TRIGGER_EVENT);
+ mMessageId = getString(wrappedData, OmtpConstants.MESSAGE_UID);
+ mMessageLength = getInt(wrappedData, OmtpConstants.MESSAGE_LENGTH);
+ mContentType = getString(wrappedData, OmtpConstants.CONTENT_TYPE);
+ mSender = getString(wrappedData, OmtpConstants.SENDER);
+ mNewMessageCount = getInt(wrappedData, OmtpConstants.NUM_MESSAGE_COUNT);
mMsgTimeMillis = parseTime(wrappedData.getString(OmtpConstants.TIME));
}
- static Long parseTime(String value) {
+ private static long parseTime(@Nullable String value) {
+ if (value == null) {
+ return 0L;
+ }
try {
return new SimpleDateFormat(
OmtpConstants.DATE_TIME_FORMAT, Locale.US)
@@ -92,8 +90,9 @@
/**
* @return the number of new messages stored on the voicemail server.
*/
+ @NeededForTesting
public int getNewMessageCount() {
- return mNewMessageCount != null ? mNewMessageCount : 0;
+ return mNewMessageCount;
}
/**
@@ -112,6 +111,7 @@
* Expected to be set only for
* {@link com.android.phone.vvm.omtp.OmtpConstants#NEW_MESSAGE}
*/
+ @NeededForTesting
public String getContentType() {
return mContentType;
}
@@ -123,7 +123,7 @@
* {@link com.android.phone.vvm.omtp.OmtpConstants#NEW_MESSAGE}
*/
public int getLength() {
- return mMessageLength != null ? mMessageLength : 0;
+ return mMessageLength;
}
/**
@@ -143,6 +143,26 @@
* {@link com.android.phone.vvm.omtp.OmtpConstants#NEW_MESSAGE}
*/
public long getTimestampMillis() {
- return mMsgTimeMillis != null ? mMsgTimeMillis : 0;
+ return mMsgTimeMillis;
+ }
+
+ private static int getInt(Bundle wrappedData, String key) {
+ String value = wrappedData.getString(key);
+ if (value == null) {
+ return 0;
+ }
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ return 0;
+ }
+ }
+
+ private static String getString(Bundle wrappedData, String key) {
+ String value = wrappedData.getString(key);
+ if (value == null) {
+ return "";
+ }
+ return value;
}
}
\ No newline at end of file
diff --git a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncReceiver.java b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncReceiver.java
index 0902b6d..415fc91 100644
--- a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncReceiver.java
+++ b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncReceiver.java
@@ -20,7 +20,8 @@
import android.content.Context;
import android.content.Intent;
import android.provider.VoicemailContract;
-import android.util.Log;
+
+import com.android.phone.vvm.omtp.VvmLog;
public class OmtpVvmSyncReceiver extends BroadcastReceiver {
@@ -29,7 +30,7 @@
@Override
public void onReceive(final Context context, Intent intent) {
if (VoicemailContract.ACTION_SYNC_VOICEMAIL.equals(intent.getAction())) {
- Log.v(TAG, "Sync intent received");
+ VvmLog.v(TAG, "Sync intent received");
Intent syncIntent = OmtpVvmSyncService
.getSyncIntent(context, OmtpVvmSyncService.SYNC_FULL_SYNC, null, true);
intent.putExtra(OmtpVvmSyncService.EXTRA_IS_MANUAL_SYNC, true);
diff --git a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
index c0411ec..74b1f66 100644
--- a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
+++ b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
@@ -27,14 +27,13 @@
import android.telecom.PhoneAccountHandle;
import android.telecom.Voicemail;
import android.text.TextUtils;
-import android.util.Log;
import com.android.phone.PhoneUtils;
import com.android.phone.VoicemailStatus;
import com.android.phone.settings.VisualVoicemailSettingsUtil;
-import com.android.phone.vvm.omtp.LocalLogHelper;
import com.android.phone.vvm.omtp.OmtpEvents;
import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
import com.android.phone.vvm.omtp.fetch.VoicemailFetchedCallback;
import com.android.phone.vvm.omtp.imap.ImapHelper;
@@ -169,21 +168,21 @@
@Override
protected void onHandleIntent(Intent intent) {
if (intent == null) {
- Log.d(TAG, "onHandleIntent: could not handle null intent");
+ VvmLog.d(TAG, "onHandleIntent: could not handle null intent");
return;
}
String action = intent.getAction();
PhoneAccountHandle phoneAccount = intent.getParcelableExtra(EXTRA_PHONE_ACCOUNT);
- LocalLogHelper.log(TAG, "Sync requested: " + action +
+ VvmLog.log(TAG, "Sync requested: " + action +
" for all accounts: " + String.valueOf(phoneAccount == null));
boolean isManualSync = intent.getBooleanExtra(EXTRA_IS_MANUAL_SYNC, false);
Voicemail voicemail = intent.getParcelableExtra(EXTRA_VOICEMAIL);
if (phoneAccount != null) {
- Log.v(TAG, "Sync requested: " + action + " - for account: " + phoneAccount);
+ VvmLog.v(TAG, "Sync requested: " + action + " - for account: " + phoneAccount);
setupAndSendRequest(phoneAccount, voicemail, action, isManualSync);
} else {
- Log.v(TAG, "Sync requested: " + action + " - for all accounts");
+ VvmLog.v(TAG, "Sync requested: " + action + " - for all accounts");
OmtpVvmSourceManager vvmSourceManager =
OmtpVvmSourceManager.getInstance(this);
Set<PhoneAccountHandle> sources = vvmSourceManager.getOmtpVvmSources();
@@ -196,7 +195,7 @@
private void setupAndSendRequest(PhoneAccountHandle phoneAccount, Voicemail voicemail,
String action, boolean isManualSync) {
if (!VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(this, phoneAccount)) {
- Log.v(TAG, "Sync requested for disabled account");
+ VvmLog.v(TAG, "Sync requested for disabled account");
return;
}
@@ -208,7 +207,7 @@
: MINIMUM_MANUAL_SYNC_INTERVAL_MILLIS;
if (currentTime - lastSyncTime < minimumInterval) {
// If it's been less than a minute since the last sync, bail.
- Log.v(TAG, "Avoiding duplicate full sync: synced recently for "
+ VvmLog.v(TAG, "Avoiding duplicate full sync: synced recently for "
+ phoneAccount.getId());
/**
@@ -238,7 +237,7 @@
while (retryCount > 0) {
ImapHelper imapHelper = new ImapHelper(this, phoneAccount, network);
if (!imapHelper.isSuccessfullyInitialized()) {
- Log.w(TAG, "Can't retrieve Imap credentials.");
+ VvmLog.w(TAG, "Can't retrieve Imap credentials.");
VisualVoicemailSettingsUtil.resetVisualVoicemailRetryInterval(this,
phoneAccount);
return;
@@ -257,7 +256,7 @@
if (VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(this, phoneAccount) &&
!success) {
retryCount--;
- Log.v(TAG, "Retrying " + action);
+ VvmLog.v(TAG, "Retrying " + action);
} else {
// Nothing more to do here, just exit.
VisualVoicemailSettingsUtil.resetVisualVoicemailRetryInterval(this,
@@ -285,7 +284,7 @@
downloadSuccess = download(imapHelper, account);
}
- Log.v(TAG, "upload succeeded: [" + String.valueOf(uploadSuccess)
+ VvmLog.v(TAG, "upload succeeded: [" + String.valueOf(uploadSuccess)
+ "] download succeeded: [" + String.valueOf(downloadSuccess) + "]");
boolean success = uploadSuccess && downloadSuccess;
@@ -330,9 +329,9 @@
super.onAvailable(network);
NetworkInfo info = getConnectivityManager().getNetworkInfo(network);
if (info == null) {
- Log.d(TAG, "Network Type: Unknown");
+ VvmLog.d(TAG, "Network Type: Unknown");
} else {
- Log.d(TAG, "Network Type: " + info.getTypeName());
+ VvmLog.d(TAG, "Network Type: " + info.getTypeName());
}
doSync(network, this, mPhoneAccount, mVoicemail, mAction);
@@ -428,7 +427,7 @@
long retryInterval = VisualVoicemailSettingsUtil.getVisualVoicemailRetryInterval(this,
phoneAccount);
- Log.v(TAG, "Retrying " + action + " in " + retryInterval + "ms");
+ VvmLog.v(TAG, "Retrying " + action + " in " + retryInterval + "ms");
AlarmManager alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + retryInterval,
diff --git a/src/com/android/phone/vvm/omtp/sync/VvmNetworkRequestCallback.java b/src/com/android/phone/vvm/omtp/sync/VvmNetworkRequestCallback.java
index 4e09527..11526ce 100644
--- a/src/com/android/phone/vvm/omtp/sync/VvmNetworkRequestCallback.java
+++ b/src/com/android/phone/vvm/omtp/sync/VvmNetworkRequestCallback.java
@@ -24,11 +24,11 @@
import android.os.Handler;
import android.os.Looper;
import android.telecom.PhoneAccountHandle;
-import android.util.Log;
import com.android.phone.PhoneUtils;
import com.android.phone.vvm.omtp.OmtpEvents;
import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
/**
* Base class for network request call backs for visual voicemail syncing with the Imap server. This
@@ -80,11 +80,11 @@
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
if (mCarrierConfigHelper.isCellularDataRequired()) {
- Log.d(TAG, "Transport type: CELLULAR");
+ VvmLog.d(TAG, "Transport type: CELLULAR");
builder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setNetworkSpecifier(Integer.toString(mSubId));
} else {
- Log.d(TAG, "Transport type: ANY");
+ VvmLog.d(TAG, "Transport type: ANY");
}
return builder.build();
}
@@ -96,7 +96,7 @@
@Override
@CallSuper
public void onLost(Network network) {
- Log.d(TAG, "onLost");
+ VvmLog.d(TAG, "onLost");
mResultReceived = true;
onFailed(NETWORK_REQUEST_FAILED_LOST);
}
@@ -117,7 +117,7 @@
public void requestNetwork() {
if (mRequestSent == true) {
- Log.e(TAG, "requestNetwork() called twice");
+ VvmLog.e(TAG, "requestNetwork() called twice");
return;
}
mRequestSent = true;
@@ -138,7 +138,7 @@
}
public void releaseNetwork() {
- Log.d(TAG, "releaseNetwork");
+ VvmLog.d(TAG, "releaseNetwork");
getConnectivityManager().unregisterNetworkCallback(this);
}
@@ -152,7 +152,7 @@
@CallSuper
public void onFailed(String reason) {
- Log.d(TAG, "onFailed: " + reason);
+ VvmLog.d(TAG, "onFailed: " + reason);
if (mCarrierConfigHelper.isCellularDataRequired()) {
mCarrierConfigHelper.handleEvent(OmtpEvents.DATA_NO_CONNECTION_CELLULAR_REQUIRED);
} else {
diff --git a/src/com/android/phone/vvm/omtp/utils/VvmDumpHandler.java b/src/com/android/phone/vvm/omtp/utils/VvmDumpHandler.java
new file mode 100644
index 0000000..227cf42
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/utils/VvmDumpHandler.java
@@ -0,0 +1,32 @@
+package com.android.phone.vvm.omtp.utils;
+
+import android.content.Context;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.VvmLog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public class VvmDumpHandler {
+
+ public static void dump(Context context, FileDescriptor fd, PrintWriter writer,
+ String[] args) {
+ IndentingPrintWriter indentedWriter = new IndentingPrintWriter(writer, " ");
+ indentedWriter.println("******* OmtpVvm *******");
+ indentedWriter.println("======= Configs =======");
+ indentedWriter.increaseIndent();
+ for (PhoneAccountHandle handle : TelecomManager.from(context)
+ .getCallCapablePhoneAccounts()) {
+ int subId = PhoneAccountHandleConverter.toSubId(handle);
+ OmtpVvmCarrierConfigHelper config = new OmtpVvmCarrierConfigHelper(context, subId);
+ indentedWriter.println(config.toString());
+ }
+ indentedWriter.decreaseIndent();
+ indentedWriter.println("======== Logs =========");
+ VvmLog.dump(fd, indentedWriter, args);
+ }
+}
diff --git a/src/com/android/services/telephony/EmergencyCallHelper.java b/src/com/android/services/telephony/EmergencyCallHelper.java
index c64a649..295f4f7 100644
--- a/src/com/android/services/telephony/EmergencyCallHelper.java
+++ b/src/com/android/services/telephony/EmergencyCallHelper.java
@@ -17,18 +17,17 @@
package com.android.services.telephony;
import android.content.Context;
-
import android.content.Intent;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Message;
import android.os.UserHandle;
import android.provider.Settings;
-import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
-import com.android.internal.os.SomeArgs;
import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneFactory;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
/**
* Helper class that implements special behavior related to emergency calls. Specifically, this
@@ -36,220 +35,75 @@
* (i.e. the device is in airplane mode), by forcibly turning the radio back on, waiting for it to
* come up, and then retrying the emergency call.
*/
-public class EmergencyCallHelper {
-
- /**
- * Receives the result of the EmergencyCallHelper's attempt to turn on the radio.
- */
- interface Callback {
- void onComplete(boolean isRadioReady);
- }
-
- // Number of times to retry the call, and time between retry attempts.
- public static final int MAX_NUM_RETRIES = 5;
- public static final long TIME_BETWEEN_RETRIES_MILLIS = 5000; // msec
-
- // Handler message codes; see handleMessage()
- private static final int MSG_START_SEQUENCE = 1;
- private static final int MSG_SERVICE_STATE_CHANGED = 2;
- private static final int MSG_RETRY_TIMEOUT = 3;
+public class EmergencyCallHelper implements EmergencyCallStateListener.Callback {
private final Context mContext;
+ private EmergencyCallStateListener.Callback mCallback;
+ private List<EmergencyCallStateListener> mListeners;
+ private List<EmergencyCallStateListener> mInProgressListeners;
+ private boolean mIsEmergencyCallingEnabled;
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_START_SEQUENCE:
- SomeArgs args = (SomeArgs) msg.obj;
- Phone phone = (Phone) args.arg1;
- EmergencyCallHelper.Callback callback =
- (EmergencyCallHelper.Callback) args.arg2;
- args.recycle();
-
- startSequenceInternal(phone, callback);
- break;
- case MSG_SERVICE_STATE_CHANGED:
- onServiceStateChanged((ServiceState) ((AsyncResult) msg.obj).result);
- break;
- case MSG_RETRY_TIMEOUT:
- onRetryTimeout();
- break;
- default:
- Log.wtf(this, "handleMessage: unexpected message: %d.", msg.what);
- break;
- }
- }
- };
-
-
- private Callback mCallback; // The callback to notify upon completion.
- private Phone mPhone; // The phone that will attempt to place the call.
- private int mNumRetriesSoFar;
public EmergencyCallHelper(Context context) {
- Log.d(this, "EmergencyCallHelper constructor.");
mContext = context;
+ mInProgressListeners = new ArrayList<>(2);
}
+ private void setupListeners() {
+ if (mListeners != null) {
+ return;
+ }
+ mListeners = new ArrayList<>(2);
+ for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
+ mListeners.add(new EmergencyCallStateListener());
+ }
+ }
/**
* Starts the "turn on radio" sequence. This is the (single) external API of the
* EmergencyCallHelper class.
*
* This method kicks off the following sequence:
- * - Power on the radio.
+ * - Power on the radio for each Phone
* - Listen for the service state change event telling us the radio has come up.
- * - Retry if we've gone {@link #TIME_BETWEEN_RETRIES_MILLIS} without any response from the
- * radio.
+ * - Retry if we've gone a significant amount of time without any response from the radio.
* - Finally, clean up any leftover state.
*
* This method is safe to call from any thread, since it simply posts a message to the
* EmergencyCallHelper's handler (thus ensuring that the rest of the sequence is entirely
- * serialized, and runs only on the handler thread.)
+ * serialized, and runs on the main looper.)
*/
- public void startTurnOnRadioSequence(Phone phone, Callback callback) {
- Log.d(this, "startTurnOnRadioSequence");
-
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = phone;
- args.arg2 = callback;
- mHandler.obtainMessage(MSG_START_SEQUENCE, args).sendToTarget();
- }
-
- /**
- * Actual implementation of startTurnOnRadioSequence(), guaranteed to run on the handler thread.
- * @see #startTurnOnRadioSequence
- */
- private void startSequenceInternal(Phone phone, Callback callback) {
- Log.d(this, "startSequenceInternal()");
-
- // First of all, clean up any state left over from a prior emergency call sequence. This
- // ensures that we'll behave sanely if another startTurnOnRadioSequence() comes in while
- // we're already in the middle of the sequence.
- cleanup();
-
- mPhone = phone;
+ public void enableEmergencyCalling(EmergencyCallStateListener.Callback callback) {
+ setupListeners();
mCallback = callback;
+ mInProgressListeners.clear();
+ mIsEmergencyCallingEnabled = false;
+ for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
+ Phone phone = PhoneFactory.getPhone(i);
+ if (phone == null)
+ continue;
-
- // No need to check the current service state here, since the only reason to invoke this
- // method in the first place is if the radio is powered-off. So just go ahead and turn the
- // radio on.
-
- powerOnRadio(); // We'll get an onServiceStateChanged() callback
- // when the radio successfully comes up.
-
- // Next step: when the SERVICE_STATE_CHANGED event comes in, we'll retry the call; see
- // onServiceStateChanged(). But also, just in case, start a timer to make sure we'll retry
- // the call even if the SERVICE_STATE_CHANGED event never comes in for some reason.
- startRetryTimer();
- }
-
- /**
- * Handles the SERVICE_STATE_CHANGED event. Normally this event tells us that the radio has
- * finally come up. In that case, it's now safe to actually place the emergency call.
- */
- private void onServiceStateChanged(ServiceState state) {
- Log.d(this, "onServiceStateChanged(), new state = %s.", state);
-
- // Possible service states:
- // - STATE_IN_SERVICE // Normal operation
- // - STATE_OUT_OF_SERVICE // Still searching for an operator to register to,
- // // or no radio signal
- // - STATE_EMERGENCY_ONLY // Phone is locked; only emergency numbers are allowed
- // - STATE_POWER_OFF // Radio is explicitly powered off (airplane mode)
-
- if (isOkToCall(state.getState(), mPhone.getState())) {
- // Woo hoo! It's OK to actually place the call.
- Log.d(this, "onServiceStateChanged: ok to call!");
-
- onComplete(true);
- cleanup();
- } else {
- // The service state changed, but we're still not ready to call yet. (This probably was
- // the transition from STATE_POWER_OFF to STATE_OUT_OF_SERVICE, which happens
- // immediately after powering-on the radio.)
- //
- // So just keep waiting; we'll probably get to either STATE_IN_SERVICE or
- // STATE_EMERGENCY_ONLY very shortly. (Or even if that doesn't happen, we'll at least do
- // another retry when the RETRY_TIMEOUT event fires.)
- Log.d(this, "onServiceStateChanged: not ready to call yet, keep waiting.");
+ mInProgressListeners.add(mListeners.get(i));
+ mListeners.get(i).waitForRadioOn(phone, this);
}
+
+ powerOnRadio();
}
-
- private boolean isOkToCall(int serviceState, PhoneConstants.State phoneState) {
- // Once we reach either STATE_IN_SERVICE or STATE_EMERGENCY_ONLY, it's finally OK to place
- // the emergency call.
- return ((phoneState == PhoneConstants.State.OFFHOOK)
- || (serviceState == ServiceState.STATE_IN_SERVICE)
- || (serviceState == ServiceState.STATE_EMERGENCY_ONLY)) ||
-
- // Allow STATE_OUT_OF_SERVICE if we are at the max number of retries.
- (mNumRetriesSoFar == MAX_NUM_RETRIES &&
- serviceState == ServiceState.STATE_OUT_OF_SERVICE);
- }
-
/**
- * Handles the retry timer expiring.
- */
- private void onRetryTimeout() {
- PhoneConstants.State phoneState = mPhone.getState();
- int serviceState = mPhone.getServiceState().getState();
- Log.d(this, "onRetryTimeout(): phone state = %s, service state = %d, retries = %d.",
- phoneState, serviceState, mNumRetriesSoFar);
-
- // - If we're actually in a call, we've succeeded.
- // - Otherwise, if the radio is now on, that means we successfully got out of airplane mode
- // but somehow didn't get the service state change event. In that case, try to place the
- // call.
- // - If the radio is still powered off, try powering it on again.
-
- if (isOkToCall(serviceState, phoneState)) {
- Log.d(this, "onRetryTimeout: Radio is on. Cleaning up.");
-
- // Woo hoo -- we successfully got out of airplane mode.
- onComplete(true);
- cleanup();
- } else {
- // Uh oh; we've waited the full TIME_BETWEEN_RETRIES_MILLIS and the radio is still not
- // powered-on. Try again.
-
- mNumRetriesSoFar++;
- Log.d(this, "mNumRetriesSoFar is now " + mNumRetriesSoFar);
-
- if (mNumRetriesSoFar > MAX_NUM_RETRIES) {
- Log.w(this, "Hit MAX_NUM_RETRIES; giving up.");
- cleanup();
- } else {
- Log.d(this, "Trying (again) to turn on the radio.");
- powerOnRadio(); // Again, we'll (hopefully) get an onServiceStateChanged() callback
- // when the radio successfully comes up.
- startRetryTimer();
- }
- }
- }
-
- /**
- * Attempt to power on the radio (i.e. take the device out of airplane mode.)
- * Additionally, start listening for service state changes; we'll eventually get an
- * onServiceStateChanged() callback when the radio successfully comes up.
+ * Attempt to power on the radio (i.e. take the device out of airplane mode). We'll eventually
+ * get an onServiceStateChanged() callback when the radio successfully comes up.
*/
private void powerOnRadio() {
Log.d(this, "powerOnRadio().");
- // We're about to turn on the radio, so arrange to be notified when the sequence is
- // complete.
- registerForServiceStateChanged();
-
// If airplane mode is on, we turn it off the same way that the Settings activity turns it
// off.
if (Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 0) > 0) {
+ Settings.Global.AIRPLANE_MODE_ON, 0) > 0) {
Log.d(this, "==> Turning off airplane mode.");
// Change the system setting
Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 0);
+ Settings.Global.AIRPLANE_MODE_ON, 0);
// Post the broadcast intend for change in airplane mode
// TODO: We really should not be in charge of sending this broadcast.
@@ -258,77 +112,19 @@
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.putExtra("state", false);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- } else {
- // Otherwise, for some strange reason the radio is off (even though the Settings
- // database doesn't think we're in airplane mode.) In this case just turn the radio
- // back on.
- Log.d(this, "==> (Apparently) not in airplane mode; manually powering radio on.");
- mPhone.setRadioPower(true);
}
}
/**
- * Clean up when done with the whole sequence: either after successfully turning on the radio,
- * or after bailing out because of too many failures.
- *
- * The exact cleanup steps are:
- * - Notify callback if we still hadn't sent it a response.
- * - Double-check that we're not still registered for any telephony events
- * - Clean up any extraneous handler messages (like retry timeouts) still in the queue
- *
- * Basically this method guarantees that there will be no more activity from the
- * EmergencyCallHelper until someone kicks off the whole sequence again with another call to
- * {@link #startTurnOnRadioSequence}
- *
- * TODO: Do the work for the comment below:
- * Note we don't call this method simply after a successful call to placeCall(), since it's
- * still possible the call will disconnect very quickly with an OUT_OF_SERVICE error.
+ * This method is called from multiple Listeners on the Main Looper.
+ * Synchronization is not necessary.
*/
- private void cleanup() {
- Log.d(this, "cleanup()");
-
- // This will send a failure call back if callback has yet to be invoked. If the callback
- // was already invoked, it's a no-op.
- onComplete(false);
-
- unregisterForServiceStateChanged();
- cancelRetryTimer();
-
- // Used for unregisterForServiceStateChanged() so we null it out here instead.
- mPhone = null;
- mNumRetriesSoFar = 0;
- }
-
- private void startRetryTimer() {
- cancelRetryTimer();
- mHandler.sendEmptyMessageDelayed(MSG_RETRY_TIMEOUT, TIME_BETWEEN_RETRIES_MILLIS);
- }
-
- private void cancelRetryTimer() {
- mHandler.removeMessages(MSG_RETRY_TIMEOUT);
- }
-
- private void registerForServiceStateChanged() {
- // Unregister first, just to make sure we never register ourselves twice. (We need this
- // because Phone.registerForServiceStateChanged() does not prevent multiple registration of
- // the same handler.)
- unregisterForServiceStateChanged();
- mPhone.registerForServiceStateChanged(mHandler, MSG_SERVICE_STATE_CHANGED, null);
- }
-
- private void unregisterForServiceStateChanged() {
- // This method is safe to call even if we haven't set mPhone yet.
- if (mPhone != null) {
- mPhone.unregisterForServiceStateChanged(mHandler); // Safe even if unnecessary
- }
- mHandler.removeMessages(MSG_SERVICE_STATE_CHANGED); // Clean up any pending messages too
- }
-
- private void onComplete(boolean isRadioReady) {
- if (mCallback != null) {
- Callback tempCallback = mCallback;
- mCallback = null;
- tempCallback.onComplete(isRadioReady);
+ @Override
+ public void onComplete(EmergencyCallStateListener listener, boolean isRadioReady) {
+ mIsEmergencyCallingEnabled |= isRadioReady;
+ mInProgressListeners.remove(listener);
+ if (mCallback != null && mInProgressListeners.isEmpty()) {
+ mCallback.onComplete(null, mIsEmergencyCallingEnabled);
}
}
}
diff --git a/src/com/android/services/telephony/EmergencyCallStateListener.java b/src/com/android/services/telephony/EmergencyCallStateListener.java
new file mode 100644
index 0000000..2346a7f
--- /dev/null
+++ b/src/com/android/services/telephony/EmergencyCallStateListener.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.services.telephony;
+
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.ServiceState;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.SubscriptionController;
+
+/**
+ * Helper class that listens to a Phone's radio state and sends a callback when the radio state of
+ * that Phone is either "in service" or "emergency calls only."
+ */
+public class EmergencyCallStateListener {
+
+ /**
+ * Receives the result of the EmergencyCallStateListener's attempt to turn on the radio.
+ */
+ interface Callback {
+ void onComplete(EmergencyCallStateListener listener, boolean isRadioReady);
+ }
+
+ // Number of times to retry the call, and time between retry attempts.
+ private static int MAX_NUM_RETRIES = 5;
+ private static long TIME_BETWEEN_RETRIES_MILLIS = 5000; // msec
+
+ // Handler message codes; see handleMessage()
+ @VisibleForTesting
+ public static final int MSG_START_SEQUENCE = 1;
+ @VisibleForTesting
+ public static final int MSG_SERVICE_STATE_CHANGED = 2;
+ @VisibleForTesting
+ public static final int MSG_RETRY_TIMEOUT = 3;
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_START_SEQUENCE:
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ Phone phone = (Phone) args.arg1;
+ EmergencyCallStateListener.Callback callback =
+ (EmergencyCallStateListener.Callback) args.arg2;
+ startSequenceInternal(phone, callback);
+ } finally {
+ args.recycle();
+ }
+ break;
+ case MSG_SERVICE_STATE_CHANGED:
+ onServiceStateChanged((ServiceState) ((AsyncResult) msg.obj).result);
+ break;
+ case MSG_RETRY_TIMEOUT:
+ onRetryTimeout();
+ break;
+ default:
+ Log.wtf(this, "handleMessage: unexpected message: %d.", msg.what);
+ break;
+ }
+ }
+ };
+
+
+ private Callback mCallback; // The callback to notify upon completion.
+ private Phone mPhone; // The phone that will attempt to place the call.
+ private int mNumRetriesSoFar;
+
+ /**
+ * Starts the "wait for radio" sequence. This is the (single) external API of the
+ * EmergencyCallStateListener class.
+ *
+ * This method kicks off the following sequence:
+ * - Listen for the service state change event telling us the radio has come up.
+ * - Retry if we've gone {@link #TIME_BETWEEN_RETRIES_MILLIS} without any response from the
+ * radio.
+ * - Finally, clean up any leftover state.
+ *
+ * This method is safe to call from any thread, since it simply posts a message to the
+ * EmergencyCallStateListener's handler (thus ensuring that the rest of the sequence is entirely
+ * serialized, and runs only on the handler thread.)
+ */
+ public void waitForRadioOn(Phone phone, Callback callback) {
+ Log.d(this, "waitForRadioOn: Phone " + phone.getPhoneId());
+
+ if (mPhone != null) {
+ // If there already is an ongoing request, ignore the new one!
+ return;
+ }
+
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = phone;
+ args.arg2 = callback;
+ mHandler.obtainMessage(MSG_START_SEQUENCE, args).sendToTarget();
+ }
+
+ /**
+ * Actual implementation of waitForRadioOn(), guaranteed to run on the handler thread.
+ *
+ * @see #waitForRadioOn
+ */
+ private void startSequenceInternal(Phone phone, Callback callback) {
+ Log.d(this, "startSequenceInternal: Phone " + phone.getPhoneId());
+
+ // First of all, clean up any state left over from a prior emergency call sequence. This
+ // ensures that we'll behave sanely if another startTurnOnRadioSequence() comes in while
+ // we're already in the middle of the sequence.
+ cleanup();
+
+ mPhone = phone;
+ mCallback = callback;
+
+ registerForServiceStateChanged();
+ // Next step: when the SERVICE_STATE_CHANGED event comes in, we'll retry the call; see
+ // onServiceStateChanged(). But also, just in case, start a timer to make sure we'll retry
+ // the call even if the SERVICE_STATE_CHANGED event never comes in for some reason.
+ startRetryTimer();
+ }
+
+ /**
+ * Handles the SERVICE_STATE_CHANGED event. Normally this event tells us that the radio has
+ * finally come up. In that case, it's now safe to actually place the emergency call.
+ */
+ private void onServiceStateChanged(ServiceState state) {
+ Log.d(this, "onServiceStateChanged(), new state = %s, Phone = %s", state,
+ mPhone.getPhoneId());
+
+ // Possible service states:
+ // - STATE_IN_SERVICE // Normal operation
+ // - STATE_OUT_OF_SERVICE // Still searching for an operator to register to,
+ // // or no radio signal
+ // - STATE_EMERGENCY_ONLY // Phone is locked; only emergency numbers are allowed
+ // - STATE_POWER_OFF // Radio is explicitly powered off (airplane mode)
+
+ if (isOkToCall(state.getState())) {
+ // Woo hoo! It's OK to actually place the call.
+ Log.d(this, "onServiceStateChanged: ok to call!");
+
+ onComplete(true);
+ cleanup();
+ } else {
+ // The service state changed, but we're still not ready to call yet. (This probably was
+ // the transition from STATE_POWER_OFF to STATE_OUT_OF_SERVICE, which happens
+ // immediately after powering-on the radio.)
+ //
+ // So just keep waiting; we'll probably get to either STATE_IN_SERVICE or
+ // STATE_EMERGENCY_ONLY very shortly. (Or even if that doesn't happen, we'll at least do
+ // another retry when the RETRY_TIMEOUT event fires.)
+ Log.d(this, "onServiceStateChanged: not ready to call yet, keep waiting.");
+ }
+ }
+
+ private boolean isOkToCall(int serviceState) {
+ // Once we reach either STATE_IN_SERVICE or STATE_EMERGENCY_ONLY, it's finally OK to place
+ // the emergency call.
+ return ((mPhone.getState() == PhoneConstants.State.OFFHOOK)
+ || (serviceState == ServiceState.STATE_IN_SERVICE)
+ || (serviceState == ServiceState.STATE_EMERGENCY_ONLY))
+ // STATE_EMERGENCY_ONLY currently is not used, so we must also check the service
+ // state for emergency only calling.
+ || (serviceState == ServiceState.STATE_OUT_OF_SERVICE &&
+ mPhone.getServiceState().isEmergencyOnly())
+ // Allow STATE_OUT_OF_SERVICE if we are at the max number of retries.
+ || (mNumRetriesSoFar == MAX_NUM_RETRIES &&
+ serviceState == ServiceState.STATE_OUT_OF_SERVICE);
+ }
+
+ /**
+ * Handles the retry timer expiring.
+ */
+ private void onRetryTimeout() {
+ int serviceState = mPhone.getServiceState().getState();
+ Log.d(this, "onRetryTimeout(): phone state = %s, service state = %d, retries = %d.",
+ mPhone.getState(), serviceState, mNumRetriesSoFar);
+
+ // - If we're actually in a call, we've succeeded.
+ // - Otherwise, if the radio is now on, that means we successfully got out of airplane mode
+ // but somehow didn't get the service state change event. In that case, try to place the
+ // call.
+ // - If the radio is still powered off, try powering it on again.
+
+ if (isOkToCall(serviceState)) {
+ Log.d(this, "onRetryTimeout: Radio is on. Cleaning up.");
+
+ // Woo hoo -- we successfully got out of airplane mode.
+ onComplete(true);
+ cleanup();
+ } else {
+ // Uh oh; we've waited the full TIME_BETWEEN_RETRIES_MILLIS and the radio is still not
+ // powered-on. Try again.
+
+ mNumRetriesSoFar++;
+ Log.d(this, "mNumRetriesSoFar is now " + mNumRetriesSoFar);
+
+ if (mNumRetriesSoFar > MAX_NUM_RETRIES) {
+ Log.w(this, "Hit MAX_NUM_RETRIES; giving up.");
+ cleanup();
+ } else {
+ Log.d(this, "Trying (again) to turn on the radio.");
+ mPhone.setRadioPower(true);
+ startRetryTimer();
+ }
+ }
+ }
+
+ /**
+ * Clean up when done with the whole sequence: either after successfully turning on the radio,
+ * or after bailing out because of too many failures.
+ *
+ * The exact cleanup steps are:
+ * - Notify callback if we still hadn't sent it a response.
+ * - Double-check that we're not still registered for any telephony events
+ * - Clean up any extraneous handler messages (like retry timeouts) still in the queue
+ *
+ * Basically this method guarantees that there will be no more activity from the
+ * EmergencyCallStateListener until someone kicks off the whole sequence again with another call
+ * to {@link #waitForRadioOn}
+ *
+ * TODO: Do the work for the comment below:
+ * Note we don't call this method simply after a successful call to placeCall(), since it's
+ * still possible the call will disconnect very quickly with an OUT_OF_SERVICE error.
+ */
+ private void cleanup() {
+ Log.d(this, "cleanup()");
+
+ // This will send a failure call back if callback has yet to be invoked. If the callback
+ // was already invoked, it's a no-op.
+ onComplete(false);
+
+ unregisterForServiceStateChanged();
+ cancelRetryTimer();
+
+ // Used for unregisterForServiceStateChanged() so we null it out here instead.
+ mPhone = null;
+ mNumRetriesSoFar = 0;
+ }
+
+ private void startRetryTimer() {
+ cancelRetryTimer();
+ mHandler.sendEmptyMessageDelayed(MSG_RETRY_TIMEOUT, TIME_BETWEEN_RETRIES_MILLIS);
+ }
+
+ private void cancelRetryTimer() {
+ mHandler.removeMessages(MSG_RETRY_TIMEOUT);
+ }
+
+ private void registerForServiceStateChanged() {
+ // Unregister first, just to make sure we never register ourselves twice. (We need this
+ // because Phone.registerForServiceStateChanged() does not prevent multiple registration of
+ // the same handler.)
+ unregisterForServiceStateChanged();
+ mPhone.registerForServiceStateChanged(mHandler, MSG_SERVICE_STATE_CHANGED, null);
+ }
+
+ private void unregisterForServiceStateChanged() {
+ // This method is safe to call even if we haven't set mPhone yet.
+ if (mPhone != null) {
+ mPhone.unregisterForServiceStateChanged(mHandler); // Safe even if unnecessary
+ }
+ mHandler.removeMessages(MSG_SERVICE_STATE_CHANGED); // Clean up any pending messages too
+ }
+
+ private void onComplete(boolean isRadioReady) {
+ if (mCallback != null) {
+ Callback tempCallback = mCallback;
+ mCallback = null;
+ tempCallback.onComplete(this, isRadioReady);
+ }
+ }
+
+ @VisibleForTesting
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ @VisibleForTesting
+ public void setMaxNumRetries(int retries) {
+ MAX_NUM_RETRIES = retries;
+ }
+
+ @VisibleForTesting
+ public void setTimeBetweenRetriesMillis(long timeMs) {
+ TIME_BETWEEN_RETRIES_MILLIS = timeMs;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !getClass().equals(o.getClass())) return false;
+
+ EmergencyCallStateListener that = (EmergencyCallStateListener) o;
+
+ if (mNumRetriesSoFar != that.mNumRetriesSoFar) {
+ return false;
+ }
+ if (mCallback != null ? !mCallback.equals(that.mCallback) : that.mCallback != null) {
+ return false;
+ }
+ return mPhone != null ? mPhone.equals(that.mPhone) : that.mPhone == null;
+
+ }
+}
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 69d57d7..62bbfe2 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -808,13 +808,19 @@
}
PhoneAccountHandle phoneAccountHandle = null;
- if (mConferenceHost.getPhone() != null &&
- mConferenceHost.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
- Phone imsPhone = mConferenceHost.getPhone();
- // The phone account handle for an ImsPhone is based on the default phone (ie the
- // base GSM or CDMA phone, not on the ImsPhone itself).
- phoneAccountHandle =
- PhoneUtils.makePstnPhoneAccountHandle(imsPhone.getDefaultPhone());
+ if (mConferenceHost.getPhone() != null) {
+ if (mConferenceHost.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+ Phone imsPhone = mConferenceHost.getPhone();
+ // The phone account handle for an ImsPhone is based on the default phone (ie the
+ // base GSM or CDMA phone, not on the ImsPhone itself).
+ phoneAccountHandle =
+ PhoneUtils.makePstnPhoneAccountHandle(imsPhone.getDefaultPhone());
+ } else {
+ // In the case of SRVCC, we still need a phone account, so use the top level phone
+ // to create a phone account.
+ phoneAccountHandle = PhoneUtils.makePstnPhoneAccountHandle(
+ mConferenceHost.getPhone());
+ }
}
if (mConferenceHost.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
diff --git a/src/com/android/services/telephony/ImsConferenceController.java b/src/com/android/services/telephony/ImsConferenceController.java
index 0f9ae5d..a874674 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -78,6 +78,12 @@
Log.v(this, "onConferenceStarted");
recalculate();
}
+
+ @Override
+ public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {
+ Log.v(this, "onConferenceSupportedChanged");
+ recalculate();
+ }
};
/**
@@ -172,6 +178,7 @@
// If this connection does not support being in a conference call, then it is not
// conferenceable with any other connection.
if (!connection.isConferenceSupported()) {
+ connection.setConferenceableConnections(Collections.<Connection>emptyList());
continue;
}
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 19b1d8a..b5b23b4 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -27,7 +27,6 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.PersistableBundle;
-import android.os.ServiceManager;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
@@ -40,7 +39,6 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import com.android.internal.telephony.IPhoneSubInfo;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.phone.PhoneGlobals;
@@ -50,6 +48,7 @@
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
+import java.util.Optional;
/**
* Owns all data we have registered with Telecom including handling dynamic addition and
@@ -72,6 +71,7 @@
private boolean mIsVideoPauseSupported;
private boolean mIsMergeCallSupported;
private boolean mIsVideoConferencingSupported;
+ private boolean mIsMergeOfWifiCallsAllowedWhenVoWifiOff;
AccountEntry(Phone phone, boolean isEmergency, boolean isDummy) {
mPhone = phone;
@@ -191,6 +191,8 @@
}
mIsMergeCallSupported = isCarrierMergeCallSupported();
mIsVideoConferencingSupported = isCarrierVideoConferencingSupported();
+ mIsMergeOfWifiCallsAllowedWhenVoWifiOff =
+ isCarrierMergeOfWifiCallsAllowedWhenVoWifiOff();
if (isEmergency && mContext.getResources().getBoolean(
R.bool.config_emergency_account_emergency_calls_only)) {
@@ -312,6 +314,20 @@
}
/**
+ * Determines from carrier config whether merging of wifi calls is allowed when VoWIFI is
+ * turned off.
+ *
+ * @return {@code true} merging of wifi calls when VoWIFI is disabled should be prevented,
+ * {@code false} otherwise.
+ */
+ private boolean isCarrierMergeOfWifiCallsAllowedWhenVoWifiOff() {
+ PersistableBundle b =
+ PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+ return b != null && b.getBoolean(
+ CarrierConfigManager.KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL);
+ }
+
+ /**
* @return The {@linke PhoneAccount} extras associated with the current subscription.
*/
private Bundle getPhoneAccountExtras() {
@@ -366,6 +382,14 @@
public boolean isVideoConferencingSupported() {
return mIsVideoConferencingSupported;
}
+
+ /**
+ * Indicate whether this account allow merging of wifi calls when VoWIFI is off.
+ * @return {@code true} if allowed, {@code false} otherwise.
+ */
+ public boolean isMergeOfWifiCallsAllowedWhenVoWifiOff() {
+ return mIsMergeOfWifiCallsAllowedWhenVoWifiOff;
+ }
}
private OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
@@ -396,6 +420,7 @@
private final TelephonyManager mTelephonyManager;
private final SubscriptionManager mSubscriptionManager;
private List<AccountEntry> mAccounts = new LinkedList<AccountEntry>();
+ private Object mAccountsLock = new Object();
private int mServiceState = ServiceState.STATE_POWER_OFF;
// TODO: Remove back-pointer from app singleton to Service, since this is not a preferred
@@ -432,9 +457,11 @@
* @return {@code True} if video pausing is supported.
*/
boolean isVideoPauseSupported(PhoneAccountHandle handle) {
- for (AccountEntry entry : mAccounts) {
- if (entry.getPhoneAccountHandle().equals(handle)) {
- return entry.isVideoPauseSupported();
+ synchronized (mAccountsLock) {
+ for (AccountEntry entry : mAccounts) {
+ if (entry.getPhoneAccountHandle().equals(handle)) {
+ return entry.isVideoPauseSupported();
+ }
}
}
return false;
@@ -448,9 +475,11 @@
* @return {@code True} if merging calls is supported.
*/
boolean isMergeCallSupported(PhoneAccountHandle handle) {
- for (AccountEntry entry : mAccounts) {
- if (entry.getPhoneAccountHandle().equals(handle)) {
- return entry.isMergeCallSupported();
+ synchronized (mAccountsLock) {
+ for (AccountEntry entry : mAccounts) {
+ if (entry.getPhoneAccountHandle().equals(handle)) {
+ return entry.isMergeCallSupported();
+ }
}
}
return false;
@@ -464,15 +493,37 @@
* @return {@code True} if video conferencing is supported.
*/
boolean isVideoConferencingSupported(PhoneAccountHandle handle) {
- for (AccountEntry entry : mAccounts) {
- if (entry.getPhoneAccountHandle().equals(handle)) {
- return entry.isVideoConferencingSupported();
+ synchronized (mAccountsLock) {
+ for (AccountEntry entry : mAccounts) {
+ if (entry.getPhoneAccountHandle().equals(handle)) {
+ return entry.isVideoConferencingSupported();
+ }
}
}
return false;
}
/**
+ * Determines if the {@link AccountEntry} associated with a {@link PhoneAccountHandle} allows
+ * merging of wifi calls when VoWIFI is disabled.
+ *
+ * @param handle The {@link PhoneAccountHandle}.
+ * @return {@code True} if merging of wifi calls is allowed when VoWIFI is disabled.
+ */
+ boolean isMergeOfWifiCallsAllowedWhenVoWifiOff(final PhoneAccountHandle handle) {
+ synchronized (mAccountsLock) {
+ Optional<AccountEntry> result = mAccounts.stream().filter(
+ entry -> entry.getPhoneAccountHandle().equals(handle)).findFirst();
+
+ if (result.isPresent()) {
+ return result.get().isMergeOfWifiCallsAllowedWhenVoWifiOff();
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
* @return Reference to the {@code TelecomAccountRegistry}'s subscription manager.
*/
SubscriptionManager getSubscriptionManager() {
@@ -486,9 +537,11 @@
* @return The address.
*/
Uri getAddress(PhoneAccountHandle handle) {
- for (AccountEntry entry : mAccounts) {
- if (entry.getPhoneAccountHandle().equals(handle)) {
- return entry.mAccount.getAddress();
+ synchronized (mAccountsLock) {
+ for (AccountEntry entry : mAccounts) {
+ if (entry.getPhoneAccountHandle().equals(handle)) {
+ return entry.mAccount.getAddress();
+ }
}
}
return null;
@@ -521,9 +574,11 @@
* @return {@code True} if an entry exists.
*/
boolean hasAccountEntryForPhoneAccount(PhoneAccountHandle handle) {
- for (AccountEntry entry : mAccounts) {
- if (entry.getPhoneAccountHandle().equals(handle)) {
- return true;
+ synchronized (mAccountsLock) {
+ for (AccountEntry entry : mAccounts) {
+ if (entry.getPhoneAccountHandle().equals(handle)) {
+ return true;
+ }
}
}
return false;
@@ -562,28 +617,31 @@
final boolean phoneAccountsEnabled = mContext.getResources().getBoolean(
R.bool.config_pstn_phone_accounts_enabled);
- if (phoneAccountsEnabled) {
- for (Phone phone : phones) {
- int subscriptionId = phone.getSubId();
- Log.d(this, "Phone with subscription id %d", subscriptionId);
- if (subscriptionId >= 0) {
- mAccounts.add(new AccountEntry(phone, false /* emergency */,
- false /* isDummy */));
+ synchronized (mAccountsLock) {
+ if (phoneAccountsEnabled) {
+ for (Phone phone : phones) {
+ int subscriptionId = phone.getSubId();
+ Log.d(this, "Phone with subscription id %d", subscriptionId);
+ if (subscriptionId >= 0) {
+ mAccounts.add(new AccountEntry(phone, false /* emergency */,
+ false /* isDummy */));
+ }
}
}
- }
- // If we did not list ANY accounts, we need to provide a "default" SIM account
- // for emergency numbers since no actual SIM is needed for dialing emergency
- // numbers but a phone account is.
- if (mAccounts.isEmpty()) {
- mAccounts.add(new AccountEntry(PhoneFactory.getDefaultPhone(), true /* emergency */,
- false /* isDummy */));
- }
+ // If we did not list ANY accounts, we need to provide a "default" SIM account
+ // for emergency numbers since no actual SIM is needed for dialing emergency
+ // numbers but a phone account is.
+ if (mAccounts.isEmpty()) {
+ mAccounts.add(new AccountEntry(PhoneFactory.getDefaultPhone(), true /* emergency */,
+ false /* isDummy */));
+ }
- // Add a fake account entry.
- if (DBG && phones.length > 0 && "TRUE".equals(System.getProperty("dummy_sim"))) {
- mAccounts.add(new AccountEntry(phones[0], false /* emergency */, true /* isDummy */));
+ // Add a fake account entry.
+ if (DBG && phones.length > 0 && "TRUE".equals(System.getProperty("dummy_sim"))) {
+ mAccounts.add(new AccountEntry(phones[0], false /* emergency */,
+ true /* isDummy */));
+ }
}
// Clean up any PhoneAccounts that are no longer relevant
@@ -615,9 +673,11 @@
}
private void tearDownAccounts() {
- for (AccountEntry entry : mAccounts) {
- entry.teardown();
+ synchronized (mAccountsLock) {
+ for (AccountEntry entry : mAccounts) {
+ entry.teardown();
+ }
+ mAccounts.clear();
}
- mAccounts.clear();
}
}
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index e495de5..07cd7b5 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -27,8 +27,10 @@
import android.telecom.ConferenceParticipant;
import android.telecom.Connection;
import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
import android.telecom.StatusHints;
import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
import android.telephony.PhoneNumberUtils;
import android.util.Pair;
@@ -41,6 +43,8 @@
import com.android.internal.telephony.gsm.SuppServiceNotification;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.phone.PhoneUtils;
import com.android.phone.R;
import java.lang.Override;
@@ -158,6 +162,10 @@
case MSG_SET_VIDEO_STATE:
int videoState = (int) msg.obj;
setVideoState(videoState);
+
+ // A change to the video state of the call can influence whether or not it
+ // can be part of a conference.
+ refreshConferenceSupported();
break;
case MSG_SET_VIDEO_PROVIDER:
@@ -709,6 +717,10 @@
if (PhoneNumberUtils.isEmergencyNumber(mOriginalConnection.getAddress())) {
mTreatAsEmergencyCall = true;
}
+
+ // Changing the address of the connection can change whether it is an emergency call or
+ // not, which can impact whether it can be part of a conference.
+ refreshConferenceSupported();
}
}
@@ -759,6 +771,22 @@
}
mIsMultiParty = mOriginalConnection.isMultiparty();
+ Bundle extrasToPut = new Bundle();
+ List<String> extrasToRemove = new ArrayList<>();
+ if (mOriginalConnection.isActiveCallDisconnectedOnAnswer()) {
+ extrasToPut.putBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true);
+ } else {
+ extrasToRemove.add(Connection.EXTRA_ANSWERING_DROPS_FG_CALL);
+ }
+
+ if (!mOriginalConnection.shouldAllowAddCallDuringVideoCall()) {
+ extrasToPut.putBoolean(Connection.EXTRA_DISABLE_ADD_CALL_DURING_VIDEO_CALL, true);
+ } else {
+ extrasToRemove.add(Connection.EXTRA_DISABLE_ADD_CALL_DURING_VIDEO_CALL);
+ }
+ putExtras(extrasToPut);
+ removeExtras(extrasToRemove);
+
// updateState can set mOriginalConnection to null if its state is DISCONNECTED, so this
// should be executed *after* the above setters have run.
updateState();
@@ -1414,6 +1442,62 @@
}
/**
+ * Determines whether the connection supports conference calling. A connection supports
+ * conference calling if it:
+ * 1. Is not an emergency call.
+ * 2. Carrier supports conference calls.
+ * 3. If call is a video call, carrier supports video conference calls.
+ * 4. If call is a wifi call and VoWIFI is disabled and carrier supports merging these calls.
+ */
+ private void refreshConferenceSupported() {
+ boolean isVideoCall = VideoProfile.isVideo(getVideoState());
+ Phone phone = getPhone();
+ boolean isIms = phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS;
+ boolean isVoWifiEnabled = false;
+ if (isIms) {
+ ImsPhone imsPhone = (ImsPhone) phone;
+ isVoWifiEnabled = imsPhone.isWifiCallingEnabled();
+ }
+ PhoneAccountHandle phoneAccountHandle = isIms ? PhoneUtils
+ .makePstnPhoneAccountHandle(phone.getDefaultPhone())
+ : PhoneUtils.makePstnPhoneAccountHandle(phone);
+ TelecomAccountRegistry telecomAccountRegistry = TelecomAccountRegistry
+ .getInstance(getPhone().getContext());
+ boolean isConferencingSupported = telecomAccountRegistry
+ .isMergeCallSupported(phoneAccountHandle);
+ boolean isVideoConferencingSupported = telecomAccountRegistry
+ .isVideoConferencingSupported(phoneAccountHandle);
+ boolean isMergeOfWifiCallsAllowedWhenVoWifiOff = telecomAccountRegistry
+ .isMergeOfWifiCallsAllowedWhenVoWifiOff(phoneAccountHandle);
+
+ Log.v(this, "refreshConferenceSupported : isConfSupp=%b, isVidConfSupp=%b, " +
+ "isMergeOfWifiAllowed=%b, isWifi=%b, isVoWifiEnabled=%b", isConferencingSupported,
+ isVideoConferencingSupported, isMergeOfWifiCallsAllowedWhenVoWifiOff, isWifi(),
+ isVoWifiEnabled);
+ boolean isConferenceSupported = true;
+ if (mTreatAsEmergencyCall) {
+ isConferenceSupported = false;
+ Log.d(this, "refreshConferenceSupported = false; emergency call");
+ } else if (!isConferencingSupported) {
+ isConferenceSupported = false;
+ Log.d(this, "refreshConferenceSupported = false; carrier doesn't support conf.");
+ } else if (isVideoCall && !isVideoConferencingSupported) {
+ isConferenceSupported = false;
+ Log.d(this, "refreshConferenceSupported = false; video conf not supported.");
+ } else if (!isMergeOfWifiCallsAllowedWhenVoWifiOff && isWifi() && !isVoWifiEnabled) {
+ isConferenceSupported = false;
+ Log.d(this,
+ "refreshConferenceSupported = false; can't merge wifi calls when voWifi off.");
+ } else {
+ Log.d(this, "refreshConferenceSupported = true.");
+ }
+
+ if (isConferenceSupported != isConferenceSupported()) {
+ setConferenceSupported(isConferenceSupported);
+ notifyConferenceSupportedChanged(isConferenceSupported);
+ }
+ }
+ /**
* Provides a mapping from extras keys which may be found in the
* {@link com.android.internal.telephony.Connection} to their equivalents defined in
* {@link android.telecom.Connection}.
@@ -1466,6 +1550,8 @@
} else {
sb.append("Y");
}
+ sb.append(" confSupported:");
+ sb.append(mIsConferenceSupported ? "Y" : "N");
sb.append("]");
return sb.toString();
}
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index a4434dd..31d0475 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -175,10 +175,83 @@
}
}
- boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(this, number);
+ final boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(this, number);
- // Get the right phone object from the account data passed in.
- final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber);
+ if (isEmergencyNumber && !isRadioOn()) {
+ final Uri emergencyHandle = handle;
+ // By default, Connection based on the default Phone, since we need to return to Telecom
+ // now.
+ final int defaultPhoneType = PhoneFactory.getDefaultPhone().getPhoneType();
+ final Connection emergencyConnection = getTelephonyConnection(request, number,
+ isEmergencyNumber, emergencyHandle, PhoneFactory.getDefaultPhone());
+ if (mEmergencyCallHelper == null) {
+ mEmergencyCallHelper = new EmergencyCallHelper(this);
+ }
+ mEmergencyCallHelper.enableEmergencyCalling(new EmergencyCallStateListener.Callback() {
+ @Override
+ public void onComplete(EmergencyCallStateListener listener, boolean isRadioReady) {
+ if (isRadioReady) {
+ // Get the right phone object since the radio has been turned on
+ // successfully.
+ final Phone phone = getPhoneForAccount(request.getAccountHandle(),
+ isEmergencyNumber);
+ // If the PhoneType of the Phone being used is different than the Default
+ // Phone, then we need create a new Connection using that PhoneType and
+ // replace it in Telecom.
+ if (phone.getPhoneType() != defaultPhoneType) {
+ Connection repConnection = getTelephonyConnection(request, number,
+ isEmergencyNumber, emergencyHandle, phone);
+ // If there was a failure, the resulting connection will not be a
+ // TelephonyConnection, so don't place the call, just return!
+ if (repConnection instanceof TelephonyConnection) {
+ placeOutgoingConnection((TelephonyConnection) repConnection, phone,
+ request);
+ }
+ // Notify Telecom of the new Connection type.
+ // TODO: Switch out the underlying connection instead of creating a new
+ // one and causing UI Jank.
+ addExistingConnection(PhoneUtils.makePstnPhoneAccountHandle(phone),
+ repConnection);
+ // Remove the old connection from Telecom after.
+ emergencyConnection.setDisconnected(
+ DisconnectCauseUtil.toTelecomDisconnectCause(
+ android.telephony.DisconnectCause.OUTGOING_CANCELED,
+ "Reconnecting outgoing Emergency Call."));
+ emergencyConnection.destroy();
+ } else {
+ placeOutgoingConnection((TelephonyConnection) emergencyConnection,
+ phone, request);
+ }
+ } else {
+ Log.w(this, "onCreateOutgoingConnection, failed to turn on radio");
+ emergencyConnection.setDisconnected(
+ DisconnectCauseUtil.toTelecomDisconnectCause(
+ android.telephony.DisconnectCause.POWER_OFF,
+ "Failed to turn on radio."));
+ emergencyConnection.destroy();
+ }
+ }
+ });
+ // Return the still unconnected GsmConnection and wait for the Radios to boot before
+ // connecting it to the underlying Phone.
+ return emergencyConnection;
+ } else {
+ // Get the right phone object from the account data passed in.
+ final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber);
+ Connection resultConnection = getTelephonyConnection(request, number, isEmergencyNumber,
+ handle, phone);
+ // If there was a failure, the resulting connection will not be a TelephonyConnection,
+ // so don't place the call!
+ if(resultConnection instanceof TelephonyConnection) {
+ placeOutgoingConnection((TelephonyConnection) resultConnection, phone, request);
+ }
+ return resultConnection;
+ }
+ }
+
+ private Connection getTelephonyConnection(final ConnectionRequest request, final String number,
+ boolean isEmergencyNumber, final Uri handle, Phone phone) {
+
if (phone == null) {
final Context context = getApplicationContext();
if (context.getResources().getBoolean(R.bool.config_checkSimStateBeforeOutgoingCall)) {
@@ -222,11 +295,12 @@
// when voice RAT is OOS but Data RAT is present.
int state = phone.getServiceState().getState();
if (state == ServiceState.STATE_OUT_OF_SERVICE) {
- if (phone.getServiceState().getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE) {
+ int dataNetType = phone.getServiceState().getDataNetworkType();
+ if (dataNetType == TelephonyManager.NETWORK_TYPE_LTE ||
+ dataNetType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
state = phone.getServiceState().getDataRegState();
}
}
- boolean useEmergencyCallHelper = false;
// If we're dialing a non-emergency number and the phone is in ECM mode, reject the call if
// carrier configuration specifies that we cannot make non-emergency calls in ECM mode.
@@ -248,11 +322,7 @@
}
}
- if (isEmergencyNumber) {
- if (!phone.isRadioOn()) {
- useEmergencyCallHelper = true;
- }
- } else {
+ if (!isEmergencyNumber) {
switch (state) {
case ServiceState.STATE_IN_SERVICE:
case ServiceState.STATE_EMERGENCY_ONLY:
@@ -307,34 +377,6 @@
connection.setInitializing();
connection.setVideoState(request.getVideoState());
- if (useEmergencyCallHelper) {
- if (mEmergencyCallHelper == null) {
- mEmergencyCallHelper = new EmergencyCallHelper(this);
- }
- mEmergencyCallHelper.startTurnOnRadioSequence(phone,
- new EmergencyCallHelper.Callback() {
- @Override
- public void onComplete(boolean isRadioReady) {
- if (connection.getState() == Connection.STATE_DISCONNECTED) {
- // If the connection has already been disconnected, do nothing.
- } else if (isRadioReady) {
- connection.setInitialized();
- placeOutgoingConnection(connection, phone, request);
- } else {
- Log.d(this, "onCreateOutgoingConnection, failed to turn on radio");
- connection.setDisconnected(
- DisconnectCauseUtil.toTelecomDisconnectCause(
- android.telephony.DisconnectCause.POWER_OFF,
- "Failed to turn on radio."));
- connection.destroy();
- }
- }
- });
-
- } else {
- placeOutgoingConnection(connection, phone, request);
- }
-
return connection;
}
@@ -512,6 +554,14 @@
}
+ private boolean isRadioOn() {
+ boolean result = false;
+ for (Phone phone : PhoneFactory.getPhones()) {
+ result |= phone.isRadioOn();
+ }
+ return result;
+ }
+
private void placeOutgoingConnection(
TelephonyConnection connection, Phone phone, ConnectionRequest request) {
String number = connection.getAddress().getSchemeSpecificPart();
@@ -573,15 +623,6 @@
returnConnection.setVideoPauseSupported(
TelecomAccountRegistry.getInstance(this).isVideoPauseSupported(
phoneAccountHandle));
- boolean isEmergencyCall = (address != null && PhoneNumberUtils.isEmergencyNumber(
- address.getSchemeSpecificPart()));
- boolean isVideoCall = VideoProfile.isVideo(videoState);
- boolean isConferencingSupported = TelecomAccountRegistry.getInstance(this)
- .isMergeCallSupported(phoneAccountHandle);
- boolean isVideoConferencingSupported = TelecomAccountRegistry.getInstance(this)
- .isVideoConferencingSupported(phoneAccountHandle);
- returnConnection.setConferenceSupported(!isEmergencyCall && isConferencingSupported
- && (!isVideoCall || (isVideoCall && isVideoConferencingSupported)));
}
return returnConnection;
}
diff --git a/tests/Android.mk b/tests/Android.mk
index 6cc0355..e1b564f 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -25,6 +25,8 @@
LOCAL_MODULE_TAGS := tests
+LOCAL_JAVA_LIBRARIES := telephony-common
+
LOCAL_INSTRUMENTATION_FOR := TeleService
LOCAL_STATIC_JAVA_LIBRARIES := \
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 8900568..cae4c1b 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -62,17 +62,16 @@
</application>
<!--
- The prefered way is to use 'runtest':
- runtest phone-unit
+ To run all tests:
+ adb shell am instrument -w
+ com.android.phone.tests/android.support.test.runner.AndroidJUnitRunner
- runtest is a wrapper around 'adb shell'. The low level shell command is:
- adb shell am instrument -w com.android.phone.tests/android.test.InstrumentationTestRunner
+ To run a single class test:
+ adb shell am instrument -e class com.android.phone.unit.FooUnitTest
+ -w com.android.phone.tests/android.support.test.runner.AndroidJUnitRunner
- To run a single test case:
- adb shell am instrument -w com.android.phone.tests/android.test.InstrumentationTestRunner
- -e com.android.phone.unit.FooUnitTest
-->
- <instrumentation android:name="android.test.InstrumentationTestRunner"
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.phone"
android:label="Phone application tests." />
</manifest>
diff --git a/tests/src/com/android/TelephonyTestBase.java b/tests/src/com/android/TelephonyTestBase.java
new file mode 100644
index 0000000..6dee12b
--- /dev/null
+++ b/tests/src/com/android/TelephonyTestBase.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android;
+
+import android.content.Context;
+import android.os.Handler;
+import android.support.test.InstrumentationRegistry;
+
+import com.android.phone.MockitoHelper;
+
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Helper class to load Mockito Resources into a test.
+ */
+public class TelephonyTestBase {
+
+ protected Context mContext;
+ MockitoHelper mMockitoHelper = new MockitoHelper();
+
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mMockitoHelper.setUp(mContext, getClass());
+ MockitoAnnotations.initMocks(this);
+ }
+
+ public void tearDown() throws Exception {
+ mMockitoHelper.tearDown();
+ }
+
+ protected final void waitForHandlerAction(Handler h, long timeoutMillis) {
+ final CountDownLatch lock = new CountDownLatch(1);
+ h.post(lock::countDown);
+ while (lock.getCount() > 0) {
+ try {
+ lock.await(timeoutMillis, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ // do nothing
+ }
+ }
+ }
+
+ protected final void waitForHandlerActionDelayed(Handler h, long timeoutMillis, long delayMs) {
+ final CountDownLatch lock = new CountDownLatch(1);
+ h.postDelayed(lock::countDown, delayMs);
+ while (lock.getCount() > 0) {
+ try {
+ lock.await(timeoutMillis, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ // do nothing
+ }
+ }
+ }
+}
diff --git a/tests/src/com/android/phone/MockitoHelper.java b/tests/src/com/android/phone/MockitoHelper.java
index 3da5d6e..7998030 100644
--- a/tests/src/com/android/phone/MockitoHelper.java
+++ b/tests/src/com/android/phone/MockitoHelper.java
@@ -16,6 +16,8 @@
package com.android.phone;
+import android.content.Context;
+
import com.android.services.telephony.Log;
/**
@@ -24,6 +26,7 @@
public final class MockitoHelper {
private static final String TAG = "MockitoHelper";
+ private static final String DEXCACHE = "dexmaker.dexcache";
private ClassLoader mOriginalClassLoader;
private Thread mContextThread;
@@ -34,7 +37,7 @@
*
* @param packageClass test case class
*/
- public void setUp(Class<?> packageClass) throws Exception {
+ public void setUp(Context context, Class<?> packageClass) throws Exception {
// makes a copy of the context classloader
mContextThread = Thread.currentThread();
mOriginalClassLoader = mContextThread.getContextClassLoader();
@@ -42,6 +45,9 @@
Log.v(TAG, "Changing context classloader from " + mOriginalClassLoader
+ " to " + newClassLoader);
mContextThread.setContextClassLoader(newClassLoader);
+ String dexCache = context.getCacheDir().toString();
+ Log.v(this, "Setting property %s to %s", DEXCACHE, dexCache);
+ System.setProperty(DEXCACHE, dexCache);
}
/**
@@ -50,5 +56,6 @@
public void tearDown() throws Exception {
Log.v(TAG, "Restoring context classloader to " + mOriginalClassLoader);
mContextThread.setContextClassLoader(mOriginalClassLoader);
+ System.clearProperty(DEXCACHE);
}
}
\ No newline at end of file
diff --git a/tests/src/com/android/phone/common/mail/MailTransportTest.java b/tests/src/com/android/phone/common/mail/MailTransportTest.java
index 6acd517..9eaef6b 100644
--- a/tests/src/com/android/phone/common/mail/MailTransportTest.java
+++ b/tests/src/com/android/phone/common/mail/MailTransportTest.java
@@ -61,7 +61,7 @@
@Override
public void setUp() throws Exception {
super.setUp();
- mMokitoHelper.setUp(getClass());
+ mMokitoHelper.setUp(getContext(), getClass());
MockitoAnnotations.initMocks(this);
}
diff --git a/tests/src/com/android/phone/vvm/omtp/OmtpBootCompletedReceiverTests.java b/tests/src/com/android/phone/vvm/omtp/OmtpBootCompletedReceiverTests.java
deleted file mode 100644
index 1924d9f..0000000
--- a/tests/src/com/android/phone/vvm/omtp/OmtpBootCompletedReceiverTests.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2016 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.vvm.omtp;
-
-import android.content.Context;
-import android.content.Intent;
-import android.preference.PreferenceManager;
-import android.test.AndroidTestCase;
-import android.util.ArraySet;
-
-import com.android.phone.vvm.omtp.OmtpBootCompletedReceiver.SubIdProcessor;
-
-import java.util.Set;
-
-public class OmtpBootCompletedReceiverTests extends AndroidTestCase {
- OmtpBootCompletedReceiver mReceiver = new OmtpBootCompletedReceiver();
- @Override
- public void setUp() {
- }
-
- @Override
- public void tearDown() {
- PreferenceManager
- .getDefaultSharedPreferences(getContext().createDeviceProtectedStorageContext())
- .edit().clear().apply();
- }
-
- public void testReadWriteList() {
- readWriteList(new int[] {1});
- }
-
- public void testReadWriteList_Multiple() {
- readWriteList(new int[] {1, 2});
- }
-
- public void testReadWriteList_Duplicate() {
- readWriteList(new int[] {1, 1});
- }
-
- private void readWriteList(int[] values) {
- for (int value : values) {
- OmtpBootCompletedReceiver.addDeferredSubId(getContext(), value);
- }
- TestSubIdProcessor processor = new TestSubIdProcessor(values);
- mReceiver.setSubIdProcessorForTest(processor);
- Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
- mReceiver.onReceive(getContext(), intent);
- processor.assertMatch();
- // after onReceive() is called the list should be empty
- TestSubIdProcessor emptyProcessor = new TestSubIdProcessor(new int[] {});
- mReceiver.setSubIdProcessorForTest(processor);
- mReceiver.onReceive(getContext(), intent);
- processor.assertMatch();
- }
-
- private static class TestSubIdProcessor implements SubIdProcessor {
- private final Set<Integer> mExpectedSubIds;
-
- public TestSubIdProcessor(int[] expectedSubIds) {
- mExpectedSubIds = new ArraySet<>();
- for(int subId : expectedSubIds){
- mExpectedSubIds.add(subId);
- }
- }
-
- @Override
- public void process(Context context, int subId){
- assertTrue(mExpectedSubIds.contains(subId));
- mExpectedSubIds.remove(subId);
- }
-
- public void assertMatch(){
- assertTrue(mExpectedSubIds.isEmpty());
- }
- }
-}
diff --git a/tests/src/com/android/phone/vvm/omtp/StatusMessageTest.java b/tests/src/com/android/phone/vvm/omtp/StatusMessageTest.java
new file mode 100644
index 0000000..fd3aa2c
--- /dev/null
+++ b/tests/src/com/android/phone/vvm/omtp/StatusMessageTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 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.vvm.omtp;
+
+import android.os.Bundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.phone.vvm.omtp.sms.StatusMessage;
+
+import junit.framework.TestCase;
+
+@VisibleForTesting
+public class StatusMessageTest extends TestCase {
+
+ public void testStatusMessage() {
+ Bundle bundle = new Bundle();
+ bundle.putString(OmtpConstants.PROVISIONING_STATUS, "status");
+ bundle.putString(OmtpConstants.RETURN_CODE, "code");
+ bundle.putString(OmtpConstants.SUBSCRIPTION_URL, "url");
+ bundle.putString(OmtpConstants.SERVER_ADDRESS, "address");
+ bundle.putString(OmtpConstants.TUI_ACCESS_NUMBER, "tui");
+ bundle.putString(OmtpConstants.CLIENT_SMS_DESTINATION_NUMBER, "sms");
+ bundle.putString(OmtpConstants.IMAP_PORT, "1234");
+ bundle.putString(OmtpConstants.IMAP_USER_NAME, "username");
+ bundle.putString(OmtpConstants.IMAP_PASSWORD, "password");
+ bundle.putString(OmtpConstants.SMTP_PORT, "s1234");
+ bundle.putString(OmtpConstants.SMTP_USER_NAME, "susername");
+ bundle.putString(OmtpConstants.SMTP_PASSWORD, "spassword");
+
+ StatusMessage message = new StatusMessage(bundle);
+ assertEquals("status", message.getProvisioningStatus());
+ assertEquals("code", message.getReturnCode());
+ assertEquals("url", message.getSubscriptionUrl());
+ assertEquals("address", message.getServerAddress());
+ assertEquals("tui", message.getTuiAccessNumber());
+ assertEquals("sms", message.getClientSmsDestinationNumber());
+ assertEquals("1234", message.getImapPort());
+ assertEquals("username", message.getImapUserName());
+ assertEquals("password", message.getImapPassword());
+ assertEquals("s1234", message.getSmtpPort());
+ assertEquals("susername", message.getSmtpUserName());
+ assertEquals("spassword", message.getSmtpPassword());
+ }
+
+ public void testSyncMessage_EmptyBundle() {
+ StatusMessage message = new StatusMessage(new Bundle());
+ assertEquals("", message.getProvisioningStatus());
+ assertEquals("", message.getReturnCode());
+ assertEquals("", message.getSubscriptionUrl());
+ assertEquals("", message.getServerAddress());
+ assertEquals("", message.getTuiAccessNumber());
+ assertEquals("", message.getClientSmsDestinationNumber());
+ assertEquals("", message.getImapPort());
+ assertEquals("", message.getImapUserName());
+ assertEquals("", message.getImapPassword());
+ assertEquals("", message.getSmtpPort());
+ assertEquals("", message.getSmtpUserName());
+ assertEquals("", message.getSmtpPassword());
+ }
+}
diff --git a/tests/src/com/android/phone/vvm/omtp/SyncMessageTest.java b/tests/src/com/android/phone/vvm/omtp/SyncMessageTest.java
new file mode 100644
index 0000000..61ae400
--- /dev/null
+++ b/tests/src/com/android/phone/vvm/omtp/SyncMessageTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 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.vvm.omtp;
+
+import android.os.Bundle;
+
+import com.android.phone.vvm.omtp.sms.SyncMessage;
+
+import junit.framework.TestCase;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
+public class SyncMessageTest extends TestCase {
+
+ public void testSyncMessage() {
+ Bundle bundle = new Bundle();
+ bundle.putString(OmtpConstants.SYNC_TRIGGER_EVENT, "event");
+ bundle.putString(OmtpConstants.MESSAGE_UID, "uid");
+ bundle.putString(OmtpConstants.MESSAGE_LENGTH, "1");
+ bundle.putString(OmtpConstants.CONTENT_TYPE, "type");
+ bundle.putString(OmtpConstants.SENDER, "sender");
+ bundle.putString(OmtpConstants.NUM_MESSAGE_COUNT, "2");
+ bundle.putString(OmtpConstants.TIME, "29/08/1997 02:14 -0400");
+
+ SyncMessage message = new SyncMessage(bundle);
+ assertEquals("event", message.getSyncTriggerEvent());
+ assertEquals("uid", message.getId());
+ assertEquals(1, message.getLength());
+ assertEquals("type", message.getContentType());
+ assertEquals("sender", message.getSender());
+ assertEquals(2, message.getNewMessageCount());
+ try {
+ assertEquals(new SimpleDateFormat(
+ OmtpConstants.DATE_TIME_FORMAT, Locale.US)
+ .parse("29/08/1997 02:14 -0400").getTime(), message.getTimestampMillis());
+ } catch (ParseException e) {
+ throw new AssertionError(e.toString());
+ }
+ }
+
+ public void testSyncMessage_EmptyBundle() {
+ SyncMessage message = new SyncMessage(new Bundle());
+ assertEquals("", message.getSyncTriggerEvent());
+ assertEquals("", message.getId());
+ assertEquals(0, message.getLength());
+ assertEquals("", message.getContentType());
+ assertEquals("", message.getSender());
+ assertEquals(0, message.getNewMessageCount());
+ assertEquals(0, message.getTimestampMillis());
+ }
+}
diff --git a/tests/src/com/android/services/telephony/EmergencyCallStateListenerTest.java b/tests/src/com/android/services/telephony/EmergencyCallStateListenerTest.java
new file mode 100644
index 0000000..64cf052
--- /dev/null
+++ b/tests/src/com/android/services/telephony/EmergencyCallStateListenerTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.services.telephony;
+
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.telephony.ServiceState;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests the EmergencyCallStateListener, which listens to one Phone and waits until its service
+ * state changes to accepting emergency calls or in service. If it can not find a tower to camp onto
+ * for emergency calls, then it will fail after a timeout period.
+ */
+@RunWith(AndroidJUnit4.class)
+public class EmergencyCallStateListenerTest extends TelephonyTestBase {
+
+ private static final long TIMEOUT_MS = 100;
+
+ @Mock Phone mMockPhone;
+ @Mock EmergencyCallStateListener.Callback mCallback;
+ EmergencyCallStateListener mListener;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ mListener = new EmergencyCallStateListener();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mListener.getHandler().removeCallbacksAndMessages(null);
+ super.tearDown();
+ }
+
+ @Test
+ public void testRegisterForCallback() {
+ mListener.waitForRadioOn(mMockPhone, mCallback);
+
+ waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
+
+ verify(mMockPhone).unregisterForServiceStateChanged(any(Handler.class));
+ verify(mMockPhone).registerForServiceStateChanged(any(Handler.class),
+ eq(EmergencyCallStateListener.MSG_SERVICE_STATE_CHANGED), isNull());
+ }
+
+ @Test
+ public void testPhoneChangeState_InService() {
+ ServiceState state = new ServiceState();
+ state.setState(ServiceState.STATE_IN_SERVICE);
+ when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
+ mListener.waitForRadioOn(mMockPhone, mCallback);
+ waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
+
+ mListener.getHandler().obtainMessage(EmergencyCallStateListener.MSG_SERVICE_STATE_CHANGED,
+ new AsyncResult(null, state, null)).sendToTarget();
+
+ waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
+ verify(mCallback).onComplete(eq(mListener), eq(true));
+ }
+
+ @Test
+ public void testPhoneChangeState_EmergencyCalls() {
+ ServiceState state = new ServiceState();
+ state.setState(ServiceState.STATE_OUT_OF_SERVICE);
+ state.setEmergencyOnly(true);
+ when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
+ when(mMockPhone.getServiceState()).thenReturn(state);
+ mListener.waitForRadioOn(mMockPhone, mCallback);
+ waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
+
+ mListener.getHandler().obtainMessage(EmergencyCallStateListener.MSG_SERVICE_STATE_CHANGED,
+ new AsyncResult(null, state, null)).sendToTarget();
+
+ waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
+ verify(mCallback).onComplete(eq(mListener), eq(true));
+ }
+
+ @Test
+ public void testPhoneChangeState_OutOfService() {
+ ServiceState state = new ServiceState();
+ state.setState(ServiceState.STATE_OUT_OF_SERVICE);
+ when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
+ when(mMockPhone.getServiceState()).thenReturn(state);
+ mListener.waitForRadioOn(mMockPhone, mCallback);
+ waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
+
+ // Don't expect any answer, since it is not the one that we want and the timeout for giving
+ // up hasn't expired yet.
+ mListener.getHandler().obtainMessage(EmergencyCallStateListener.MSG_SERVICE_STATE_CHANGED,
+ new AsyncResult(null, state, null)).sendToTarget();
+
+ waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
+ verify(mCallback, never()).onComplete(any(EmergencyCallStateListener.class), anyBoolean());
+ }
+
+ @Test
+ public void testTimeout_EmergencyCalls() {
+ ServiceState state = new ServiceState();
+ state.setState(ServiceState.STATE_OUT_OF_SERVICE);
+ state.setEmergencyOnly(true);
+ when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
+ when(mMockPhone.getServiceState()).thenReturn(state);
+ mListener.waitForRadioOn(mMockPhone, mCallback);
+ mListener.setTimeBetweenRetriesMillis(500);
+
+ // Wait for the timer to expire and check state manually in onRetryTimeout
+ waitForHandlerActionDelayed(mListener.getHandler(), TIMEOUT_MS, 600);
+
+ verify(mCallback).onComplete(eq(mListener), eq(true));
+ }
+
+ @Test
+ public void testTimeout_RetryFailure() {
+ ServiceState state = new ServiceState();
+ state.setState(ServiceState.STATE_POWER_OFF);
+ when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
+ when(mMockPhone.getServiceState()).thenReturn(state);
+ mListener.waitForRadioOn(mMockPhone, mCallback);
+ mListener.setTimeBetweenRetriesMillis(100);
+ mListener.setMaxNumRetries(2);
+
+ // Wait for the timer to expire and check state manually in onRetryTimeout
+ waitForHandlerActionDelayed(mListener.getHandler(), TIMEOUT_MS, 600);
+
+ verify(mCallback).onComplete(eq(mListener), eq(false));
+ verify(mMockPhone, times(2)).setRadioPower(eq(true));
+ }
+
+}