Merge "Sync the wfc roaming logic with Settings" into qt-dev
diff --git a/Android.mk b/Android.mk
index 775d795..8aff191 100644
--- a/Android.mk
+++ b/Android.mk
@@ -25,7 +25,8 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
guava \
- volley
+ volley \
+ android-support-annotations
LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
LOCAL_SRC_FILES += $(call all-proto-files-under, ecc/proto)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index eca7648..938d714 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -469,6 +469,7 @@
privileged processes can start them.
-->
<activity android:name=".euicc.EuiccPrivilegedActionUiDispatcherActivity"
+ android:theme="@android:style/Theme.NoDisplay"
android:permission="android.permission.CALL_PRIVILEGED">
<!-- Max out priority to ensure nobody else will handle these intents. -->
<intent-filter android:priority="1000">
@@ -606,6 +607,14 @@
android:name="com.android.internal.telephony.uicc.ShowInstallAppNotificationReceiver"
android:exported="false"/>
+ <activity
+ android:name="com.android.phone.settings.PickSmsSubscriptionActivity"
+ android:exported="false"
+ android:excludeFromRecents="true"
+ android:launchMode="singleTop"
+ android:configChanges="orientation|screenSize|keyboardHidden"
+ android:theme="@style/Theme.Transparent"/>
+
<service
android:name="com.android.phone.vvm.RemoteVvmTaskManager"
android:exported="false"/>
diff --git a/ecc/input/eccdata.txt b/ecc/input/eccdata.txt
index 403cad7..bf5d92c 100644
--- a/ecc/input/eccdata.txt
+++ b/ecc/input/eccdata.txt
@@ -600,8 +600,11 @@
countries {
iso_code: "DE"
eccs {
- phone_number: "112"
+ phone_number: "110"
types: POLICE
+ }
+ eccs {
+ phone_number: "112"
types: AMBULANCE
types: FIRE
}
diff --git a/ecc/output/eccdata b/ecc/output/eccdata
index 679bc36..2b9007a 100644
--- a/ecc/output/eccdata
+++ b/ecc/output/eccdata
Binary files differ
diff --git a/res/layout/emergency_information.xml b/res/layout/emergency_information.xml
index e925479..524387f 100644
--- a/res/layout/emergency_information.xml
+++ b/res/layout/emergency_information.xml
@@ -53,7 +53,7 @@
android:maxLines="1"
android:ellipsize="end"
android:lineHeight="@dimen/emergency_info_name_line_height"
- android:fontFamily="google-sans"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
android:textAppearance="@style/HeadlineTextAppearance"/>
<TextView
android:id="@+id/emergency_info_hint"
@@ -99,7 +99,7 @@
android:maxLines="2"
android:ellipsize="end"
android:lineHeight="@dimen/confirmed_emergency_info_line_height"
- android:fontFamily="google-sans"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
android:textAppearance="@style/PhoneCallHintTextAppearance"
android:text="@string/emergency_information_confirm_hint"/>
</LinearLayout>
diff --git a/res/layout/emergency_shortcut_button.xml b/res/layout/emergency_shortcut_button.xml
index 2828926..3c5eaa2 100644
--- a/res/layout/emergency_shortcut_button.xml
+++ b/res/layout/emergency_shortcut_button.xml
@@ -57,7 +57,7 @@
android:maxLines="1"
android:ellipsize="end"
android:lineHeight="@dimen/phone_number_line_height"
- android:fontFamily="google-sans"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
android:textAppearance="@style/PhoneNumberTextAppearance"/>
<TextView
android:id="@+id/phone_number_description"
@@ -124,7 +124,7 @@
android:maxLines="2"
android:ellipsize="end"
android:lineHeight="@dimen/phone_call_hint_line_height"
- android:fontFamily="google-sans"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
android:textAppearance="@style/PhoneCallHintTextAppearance"/>
</FrameLayout>
</LinearLayout>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index dbc0af4..39c6d75 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -622,7 +622,7 @@
<string name="preference_category_ringtone" msgid="5197960752529332721">"रिंगटोन और कंपन"</string>
<string name="pstn_connection_service_label" msgid="1743245930577325900">"अंतर्निहित सिम कार्ड"</string>
<string name="enable_video_calling_title" msgid="7237253660669000899">"वीडियो कॉलिंग चालू करें"</string>
- <string name="enable_video_calling_dialog_msg" msgid="8948186136957417948">"वीडियो कॉलिंग चालू करने के लिए, आपको नेटवर्क सेटिंग में उन्नत 4G LTE मोड सक्षम करना होगा."</string>
+ <string name="enable_video_calling_dialog_msg" msgid="8948186136957417948">"वीडियो कॉलिंग चालू करने के लिए, आपको नेटवर्क सेटिंग में बेहतर 4G LTE मोड सक्षम करना होगा."</string>
<string name="enable_video_calling_dialog_settings" msgid="576528473599603249">"नेटवर्क सेटिंग"</string>
<string name="enable_video_calling_dialog_close" msgid="7411471282167927991">"बंद करें"</string>
<string name="sim_label_emergency_calls" msgid="4847699229529306397">"आपातकालीन कॉल"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 020f3da..5400ad4 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -592,12 +592,12 @@
<string name="phone_in_ecm_call_notification_text" msgid="4611608947314729773">"डेटा कनेक्शन अक्षम केले"</string>
<string name="phone_in_ecm_notification_complete_time" msgid="7730376844178948351">"<xliff:g id="COMPLETETIME">%s</xliff:g> पर्यंत कोणतेही डेटा कनेक्शन नाही"</string>
<plurals name="alert_dialog_exit_ecm" formatted="false" msgid="7179911675595441201">
- <item quantity="one">फोन <xliff:g id="COUNT_1">%s</xliff:g> मिनिटासाठी आणीबाणी कॉलबॅक मोडमध्ये राहील. या मोडमध्ये असताना डेटा कनेक्शन वापरणारे कोणतेही अॅप्लिकेशन वापरले जाऊ शकत नाहीत. तुम्ही आता बाहेर पडू इच्छिता?</item>
<item quantity="other">फोन <xliff:g id="COUNT_1">%s</xliff:g> मिनिटांसाठी आणीबाणी कॉलबॅक मोडमध्ये राहील. या मोडमध्ये असताना डेटा कनेक्शन वापरणारे कोणतेही अॅप्लिकेशन वापरले जाऊ शकत नाहीत. तुम्ही आता बाहेर पडू इच्छिता?</item>
+ <item quantity="one">फोन <xliff:g id="COUNT_0">%s</xliff:g> मिनिटासाठी आणीबाणी कॉलबॅक मोडमध्ये राहील. या मोडमध्ये असताना डेटा कनेक्शन वापरणारे कोणतेही अॅप वापरले जाऊ शकत नाहीत. तुम्ही आता बाहेर पडू इच्छिता?</item>
</plurals>
<plurals name="alert_dialog_not_avaialble_in_ecm" formatted="false" msgid="8042973425225093895">
- <item quantity="one">आणीबाणी कॉलबॅक मोडमध्ये असताना निवडलेली क्रिया उपलब्ध नसते. फोन <xliff:g id="COUNT_1">%s</xliff:g> मिनिटासाठी या मोडमध्ये राहील. तुम्ही आता बाहेर पडू इच्छिता?</item>
<item quantity="other">आणीबाणी कॉलबॅक मोडमध्ये असताना निवडलेली क्रिया उपलब्ध नसते. फोन <xliff:g id="COUNT_1">%s</xliff:g> मिनिटांसाठी या मोडमध्ये राहील. तुम्ही आता बाहेर पडू इच्छिता?</item>
+ <item quantity="one">आणीबाणी कॉलबॅक मोडमध्ये असताना निवडलेली क्रिया उपलब्ध नसते. फोन <xliff:g id="COUNT_0">%s</xliff:g> मिनिटासाठी या मोडमध्ये राहील. तुम्ही आता बाहेर पडू इच्छिता?</item>
</plurals>
<string name="alert_dialog_in_ecm_call" msgid="1886723687211887104">"आणीबाणीच्या कॉलमध्ये असताना निवडलेली क्रिया उपलब्ध नसते."</string>
<string name="progress_dialog_exiting_ecm" msgid="4835734101617817074">"आणीबाणी कॉलबॅक मोडमधून बाहेर पडत आहे"</string>
@@ -762,8 +762,8 @@
<string name="supp_service_outgoing_calls_barred" msgid="1962644621292054081">"जाणारे कॉल अवरोधित केले."</string>
<string name="supp_service_call_forwarding_active" msgid="1253134771682248735">"कॉल फॉरवर्ड होत आहे."</string>
<string name="supp_service_additional_call_forwarded" msgid="5228624725214727315">"अतिरिक्त कॉल फॉरवर्ड केला."</string>
- <string name="supp_service_additional_ect_connected" msgid="6396964292513707102">"भडक कॉल ट्रांसफर करणे पूर्ण झाले."</string>
- <string name="supp_service_additional_ect_connecting" msgid="5443373059716058480">"भडक कॉल ट्रांसफर होत आहे."</string>
+ <string name="supp_service_additional_ect_connected" msgid="6396964292513707102">"भडक कॉल ट्रान्सफर करणे पूर्ण झाले."</string>
+ <string name="supp_service_additional_ect_connecting" msgid="5443373059716058480">"भडक कॉल ट्रान्सफर होत आहे."</string>
<string name="supp_service_call_on_hold" msgid="1478976782361795422">"कॉल होल्डवर आहे."</string>
<string name="supp_service_call_resumed" msgid="1656475289958070674">"कॉल पुन्हा सुरू झाला."</string>
<string name="supp_service_deflected_call" msgid="138970419873492166">"कॉल वळवला."</string>
diff --git a/res/xml/carrier_ss_string.xml b/res/xml/carrier_ss_string.xml
index 96bcfa4..826f0ea 100644
--- a/res/xml/carrier_ss_string.xml
+++ b/res/xml/carrier_ss_string.xml
@@ -74,10 +74,10 @@
<resources>
<!-- This is a parser format by Regular Expression.
"response_format"'s position follow below index.
- ((\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?)?)?)?#)
- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+ ((\d{2,3})(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*))?)?)?)?)?)?)?#)
+ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
-->
- <regular_parser>((\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?)?)?)?#)</regular_parser>
+ <regular_parser>((\d{2,3})(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*))?)?)?)?)?)?)?#)</regular_parser>
<feature name="callforwarding">
<command name="query"><!--For example: *#120*1#-->
<service_code>120</service_code>
diff --git a/res/xml/carrier_ss_string_850.xml b/res/xml/carrier_ss_string_850.xml
index 53611d4..86d5743 100644
--- a/res/xml/carrier_ss_string_850.xml
+++ b/res/xml/carrier_ss_string_850.xml
@@ -22,10 +22,10 @@
<resources>
<!-- This is a parser format by Regular Expression.
"response_format"'s position follow below index.
- ((\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?)?)?)?#)
- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+ ((\d{2,3})(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*))?)?)?)?)?)?)?#)
+ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
-->
- <regular_parser>((\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?)?)?)?#)</regular_parser>
+ <regular_parser>((\d{2,3})(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*)(\*([^*#]*))?)?)?)?)?)?)?#)</regular_parser>
<feature name="callforwarding">
<command name="query"><!--For example: *#120*1#-->
<service_code>120</service_code>
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index f1db076..f2438d9 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -407,7 +407,8 @@
prefSet.removePreference(mEnableVideoCalling);
}
- final PhoneAccountHandle simCallManager = mTelecomManager.getSimCallManager();
+ final PhoneAccountHandle simCallManager = mTelecomManager.getSimCallManagerForSubscription(
+ mPhone.getSubId());
if (simCallManager != null) {
Intent intent = PhoneAccountSettingsFragment.buildPhoneAccountConfigureIntent(
this, simCallManager);
diff --git a/src/com/android/phone/CarrierXmlParser.java b/src/com/android/phone/CarrierXmlParser.java
index 4cf7e35..18602c9 100644
--- a/src/com/android/phone/CarrierXmlParser.java
+++ b/src/com/android/phone/CarrierXmlParser.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.XmlResourceParser;
+import android.support.annotation.VisibleForTesting;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
@@ -95,7 +96,9 @@
private static final String ATTR_DEFINITION_KEY = "definition";
HashMap<String, SsFeature> mFeatureMaps;
- private static String sParserFormat = "";
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ static String sParserFormat = "";
// TAG_ENTRY_NUMBER and TAG_ENTRY_TIME is xml's entry value.
// This is mapping user's input value. For example: number,time ...
@@ -155,6 +158,8 @@
mParserStr.add("");
}
}
+ } else {
+ Log.d(LOG_TAG, "no match");
}
}
diff --git a/src/com/android/phone/MobileNetworkSettings.java b/src/com/android/phone/MobileNetworkSettings.java
index c123e66..e67758d 100644
--- a/src/com/android/phone/MobileNetworkSettings.java
+++ b/src/com/android/phone/MobileNetworkSettings.java
@@ -149,12 +149,17 @@
/**
- * Returns true if Wifi calling is enabled for at least one phone.
+ * Returns true if Wifi calling is enabled for at least one subscription.
*/
public static boolean isWifiCallingEnabled(Context context) {
- int phoneCount = TelephonyManager.from(context).getPhoneCount();
- for (int i = 0; i < phoneCount; i++) {
- if (isWifiCallingEnabled(context, i)) {
+ SubscriptionManager subManager = context.getSystemService(SubscriptionManager.class);
+ if (subManager == null) {
+ Log.e(MobileNetworkFragment.LOG_TAG,
+ "isWifiCallingEnabled: couldn't get system service.");
+ return false;
+ }
+ for (int subId : subManager.getActiveSubscriptionIdList()) {
+ if (isWifiCallingEnabled(context, subId)) {
return true;
}
}
@@ -162,11 +167,12 @@
}
/**
- * Returns true if Wifi calling is enabled for the specific phone with id {@code phoneId}.
+ * Returns true if Wifi calling is enabled for the specific subscription with id {@code subId}.
*/
- public static boolean isWifiCallingEnabled(Context context, int phoneId) {
+ public static boolean isWifiCallingEnabled(Context context, int subId) {
final PhoneAccountHandle simCallManager =
- TelecomManager.from(context).getSimCallManager();
+ TelecomManager.from(context).getSimCallManagerForSubscription(subId);
+ final int phoneId = SubscriptionManager.getSlotIndex(subId);
boolean isWifiCallingEnabled;
if (simCallManager != null) {
@@ -1890,14 +1896,14 @@
}
// Removes the preference if the wifi calling is disabled.
- if (!isWifiCallingEnabled(getContext(), SubscriptionManager.getPhoneId(mSubId))) {
+ if (!isWifiCallingEnabled(getContext(), mSubId)) {
mCallingCategory.removePreference(mWiFiCallingPref);
return;
}
// See what Telecom thinks the SIM call manager is.
final PhoneAccountHandle simCallManager =
- TelecomManager.from(getContext()).getSimCallManager();
+ TelecomManager.from(getContext()).getSimCallManagerForSubscription(mSubId);
// Check which SIM call manager is for the current sub ID.
PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index de3fffc..942c1e2 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -569,9 +569,8 @@
if (DBG) log("showDataDisconnectedRoaming()...");
// "Mobile network settings" screen / dialog
- Intent intent = new Intent(mContext, com.android.phone.MobileNetworkSettings.class);
+ Intent intent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
intent.putExtra(Settings.EXTRA_SUB_ID, subId);
- intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent contentIntent = PendingIntent.getActivity(mContext, subId, intent, 0);
final CharSequence contentText = mContext.getText(R.string.roaming_reenable_message);
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index df589fd..97f62e8 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -79,7 +79,6 @@
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
-import android.telephony.SmsManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyHistogram;
@@ -91,6 +90,7 @@
import android.telephony.VisualVoicemailSmsFilterSettings;
import android.telephony.cdma.CdmaCellLocation;
import android.telephony.data.ApnSetting;
+import android.telephony.data.ApnSetting.ApnType;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.gsm.GsmCellLocation;
import android.telephony.ims.ProvisioningManager;
@@ -121,6 +121,7 @@
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.DefaultPhoneNotifier;
import com.android.internal.telephony.HalVersion;
+import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.INumberVerificationCallback;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.IccCard;
@@ -139,6 +140,8 @@
import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.SmsApplication;
import com.android.internal.telephony.SmsApplication.SmsApplicationData;
+import com.android.internal.telephony.SmsController;
+import com.android.internal.telephony.SmsPermissions;
import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.telephony.dataconnection.ApnSettingUtils;
@@ -156,6 +159,7 @@
import com.android.internal.telephony.uicc.UiccSlot;
import com.android.internal.telephony.util.VoicemailNotificationSettingsUtil;
import com.android.internal.util.HexDump;
+import com.android.phone.settings.PickSmsSubscriptionActivity;
import com.android.phone.vvm.PhoneAccountHandleConverter;
import com.android.phone.vvm.RemoteVvmTaskManager;
import com.android.phone.vvm.VisualVoicemailSettingsUtil;
@@ -163,7 +167,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -172,6 +175,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Set;
/**
@@ -248,6 +252,8 @@
private static final int EVENT_REQUEST_CELL_INFO_UPDATE_DONE = 67;
private static final int CMD_REQUEST_ENABLE_MODEM = 68;
private static final int EVENT_ENABLE_MODEM_DONE = 69;
+ private static final int CMD_GET_MODEM_STATUS = 70;
+ private static final int EVENT_GET_MODEM_STATUS_DONE = 71;
// Parameters of select command.
private static final int SELECT_COMMAND = 0xA4;
@@ -1117,10 +1123,42 @@
ar = (AsyncResult) msg.obj;
request = (MainThreadRequest) ar.userObj;
request.result = (ar.exception == null);
+ int phoneId = request.phone.getPhoneId();
//update the cache as modem status has changed
- mPhoneConfigurationManager.addToPhoneStatusCache(
- request.phone.getPhoneId(), msg.arg1 == 1);
- updateModemStateMetrics();
+ if ((boolean) request.result) {
+ mPhoneConfigurationManager.addToPhoneStatusCache(phoneId, msg.arg1 == 1);
+ updateModemStateMetrics();
+ } else {
+ Log.e(LOG_TAG, msg.what + " failure. Not updating modem status."
+ + ar.exception);
+ }
+ notifyRequester(request);
+ break;
+ case CMD_GET_MODEM_STATUS:
+ request = (MainThreadRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_GET_MODEM_STATUS_DONE, request);
+ PhoneConfigurationManager.getInstance()
+ .getPhoneStatusFromModem(request.phone, onCompleted);
+ break;
+ case EVENT_GET_MODEM_STATUS_DONE:
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ int id = request.phone.getPhoneId();
+ if (ar.exception == null && ar.result != null) {
+ request.result = ar.result;
+ //update the cache as modem status has changed
+ mPhoneConfigurationManager.addToPhoneStatusCache(id,
+ (boolean) request.result);
+ } else {
+ // Return true if modem status cannot be retrieved. For most cases,
+ // modem status is on. And for older version modems, GET_MODEM_STATUS
+ // and disable modem are not supported. Modem is always on.
+ // TODO: this should be fixed in R to support a third
+ // status UNKNOWN b/131631629
+ request.result = true;
+ Log.e(LOG_TAG, msg.what + " failure. Not updating modem status."
+ + ar.exception);
+ }
notifyRequester(request);
break;
default:
@@ -2101,10 +2139,16 @@
case DENIED_HARD:
throw new SecurityException("Not allowed to access cell info");
case DENIED_SOFT:
+ try {
+ cb.onCellInfo(new ArrayList<CellInfo>());
+ } catch (RemoteException re) {
+ // Drop without consequences
+ }
return;
}
- final Phone phone = getPhone(subId);
+
+ final Phone phone = getPhoneFromSubId(subId);
if (phone == null) throw new IllegalArgumentException("Invalid Subscription Id: " + subId);
sendRequestAsync(CMD_REQUEST_CELL_INFO_UPDATE, cb, phone, workSource);
@@ -2613,22 +2657,11 @@
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
enforceVisualVoicemailPackage(callingPackage, subId);
enforceSendSmsPermission();
- // Make the calls as the phone process.
- final long identity = Binder.clearCallingIdentity();
- try {
- SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(subId);
- if (port == 0) {
- smsManager.sendTextMessageWithSelfPermissions(number, null, text,
- sentIntent, null, false);
- } else {
- byte[] data = text.getBytes(StandardCharsets.UTF_8);
- smsManager.sendDataMessageWithSelfPermissions(number, null,
- (short) port, data, sentIntent, null);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ SmsController smsController = PhoneFactory.getSmsController();
+ smsController.sendVisualVoicemailSmsForSubscriber(callingPackage, subId, number, port, text,
+ sentIntent);
}
+
/**
* Sets the voice activation state of a given subId.
*/
@@ -3032,10 +3065,8 @@
final long identity = Binder.clearCallingIdentity();
try {
// TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
- boolean isRoaming = TelephonyManager.from(
- getPhone(subId).getContext()).isNetworkRoaming(subId);
ImsManager.getInstance(mApp,
- getSlotIndexOrException(subId)).setWfcNonPersistent(isCapable, mode, isRoaming);
+ getSlotIndexOrException(subId)).setWfcNonPersistent(isCapable, mode);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -3813,7 +3844,7 @@
int command, int p1, int p2, int p3, String data) {
final long identity = Binder.clearCallingIdentity();
try {
- if (channel < 0) {
+ if (channel <= 0) {
return "";
}
@@ -4750,17 +4781,19 @@
}
@Override
- public int checkCarrierPrivilegesForPackage(String pkgName) {
- final Phone defaultPhone = getDefaultPhone();
- if (TextUtils.isEmpty(pkgName))
+ public int checkCarrierPrivilegesForPackage(int subId, String pkgName) {
+ if (TextUtils.isEmpty(pkgName)) {
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
- UiccCard card = UiccController.getInstance().getUiccCard(defaultPhone.getPhoneId());
+ }
+
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+ UiccCard card = UiccController.getInstance().getUiccCard(phoneId);
if (card == null) {
- loge("checkCarrierPrivilegesForPackage: No UICC");
+ loge("checkCarrierPrivilegesForPackage: No UICC on subId " + subId);
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
}
- return card.getCarrierPrivilegeStatus(defaultPhone.getContext().getPackageManager(),
- pkgName);
+
+ return card.getCarrierPrivilegeStatus(mApp.getPackageManager(), pkgName);
}
@Override
@@ -4799,29 +4832,26 @@
}
@Override
- public List<String> getPackagesWithCarrierPrivileges() {
+ public List<String> getPackagesWithCarrierPrivileges(int phoneId) {
PackageManager pm = mApp.getPackageManager();
List<String> privilegedPackages = new ArrayList<>();
List<PackageInfo> packages = null;
- for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
- UiccCard card = UiccController.getInstance().getUiccCard(i);
- if (card == null) {
- // No UICC in that slot.
- continue;
- }
+ UiccCard card = UiccController.getInstance().getUiccCard(phoneId);
+ // has UICC in that slot.
+ if (card != null) {
if (card.hasCarrierPrivilegeRules()) {
if (packages == null) {
// Only check packages in user 0 for now
packages = pm.getInstalledPackagesAsUser(
PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
- | PackageManager.GET_SIGNATURES, UserHandle.USER_SYSTEM);
+ | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+ | PackageManager.GET_SIGNATURES, UserHandle.USER_SYSTEM);
}
for (int p = packages.size() - 1; p >= 0; p--) {
PackageInfo pkgInfo = packages.get(p);
if (pkgInfo != null && pkgInfo.packageName != null
&& card.getCarrierPrivilegeStatus(pkgInfo)
- == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
privilegedPackages.add(pkgInfo.packageName);
}
}
@@ -4830,6 +4860,15 @@
return privilegedPackages;
}
+ @Override
+ public List<String> getPackagesWithCarrierPrivilegesForAllPhones() {
+ List<String> privilegedPackages = new ArrayList<>();
+ for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
+ privilegedPackages.addAll(getPackagesWithCarrierPrivileges(i));
+ }
+ return privilegedPackages;
+ }
+
private String getIccId(int subId) {
final Phone phone = getPhone(subId);
UiccCard card = phone == null ? null : phone.getUiccCard();
@@ -4945,7 +4984,7 @@
}
@Override
- public String[] getMergedSubscriberIds(String callingPackage) {
+ public String[] getMergedSubscriberIds(int subId, String callingPackage) {
// This API isn't public, so no need to provide a valid subscription ID - we're not worried
// about carrier-privileged callers not having access.
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
@@ -4954,6 +4993,9 @@
return null;
}
+ // Clear calling identity, when calling TelephonyManager, because callerUid must be
+ // the process, where TelephonyManager was instantiated.
+ // Otherwise AppOps check will fail.
final long identity = Binder.clearCallingIdentity();
try {
final Context context = mApp;
@@ -4962,12 +5004,10 @@
// Figure out what subscribers are currently active
final ArraySet<String> activeSubscriberIds = new ArraySet<>();
- // Clear calling identity, when calling TelephonyManager, because callerUid must be
- // the process, where TelephonyManager was instantiated.
- // Otherwise AppOps check will fail.
- final int[] subIds = sub.getActiveSubscriptionIdList();
- for (int subId : subIds) {
+ // Only consider subs which match the current subId
+ // This logic can be simplified. See b/131189269 for progress.
+ if (isActiveSubscription(subId)) {
activeSubscriberIds.add(tele.getSubscriberId(subId));
}
@@ -6365,11 +6405,11 @@
continue;
}
- String cardId = null;
+ String cardId;
UiccCard card = slot.getUiccCard();
if (card != null) {
cardId = card.getCardId();
- } else if (!slot.isEuicc()) {
+ } else {
cardId = slot.getIccId();
}
@@ -6821,7 +6861,11 @@
final long identity = Binder.clearCallingIdentity();
try {
- return PhoneConfigurationManager.getInstance().getPhoneStatus(phone);
+ try {
+ return mPhoneConfigurationManager.getPhoneStatusFromCache(phone.getPhoneId());
+ } catch (NoSuchElementException ex) {
+ return (Boolean) sendRequest(CMD_GET_MODEM_STATUS, null, phone, null);
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -6998,8 +7042,7 @@
Phone phone = getPhone(subId);
if (phone == null) return false;
- boolean isMetered = ApnSettingUtils.isMeteredApnType(ApnSetting.getApnTypeString(
- apnType), phone);
+ boolean isMetered = ApnSettingUtils.isMeteredApnType(apnType, phone);
return !isMetered || phone.getDataEnabledSettings().isDataEnabled(apnType);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -7007,7 +7050,7 @@
}
@Override
- public boolean isApnMetered(int apnType, int subId) {
+ public boolean isApnMetered(@ApnType int apnType, int subId) {
enforceReadPrivilegedPermission("isApnMetered");
// Now that all security checks passes, perform the operation as ourselves.
@@ -7016,10 +7059,26 @@
Phone phone = getPhone(subId);
if (phone == null) return true; // By default return true.
- return ApnSettingUtils.isMeteredApnType(ApnSetting.getApnTypeString(
- apnType), phone);
+ return ApnSettingUtils.isMeteredApnType(apnType, phone);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
+
+ @Override
+ public void enqueueSmsPickResult(String callingPackage, IIntegerConsumer pendingSubIdResult) {
+ SmsPermissions permissions = new SmsPermissions(getDefaultPhone(), mApp,
+ (AppOpsManager) mApp.getSystemService(Context.APP_OPS_SERVICE));
+ if (!permissions.checkCallingCanSendSms(callingPackage, "Sending message")) {
+ throw new SecurityException("Requires SEND_SMS permission to perform this operation");
+ }
+ PickSmsSubscriptionActivity.addPendingResult(pendingSubIdResult);
+ Intent intent = new Intent();
+ intent.setClass(mApp, PickSmsSubscriptionActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // Bring up choose default SMS subscription dialog right now
+ intent.putExtra(PickSmsSubscriptionActivity.DIALOG_TYPE_KEY,
+ PickSmsSubscriptionActivity.SMS_PICK_FOR_MESSAGE);
+ mApp.startActivity(intent);
+ }
}
diff --git a/src/com/android/phone/settings/PickSmsSubscriptionActivity.java b/src/com/android/phone/settings/PickSmsSubscriptionActivity.java
new file mode 100644
index 0000000..cfbce28
--- /dev/null
+++ b/src/com/android/phone/settings/PickSmsSubscriptionActivity.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2019 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.settings;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+
+import com.android.internal.telephony.IIntegerConsumer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Trampolines a request to Settings to get the SMS subscription associated with an SmsManager
+ * operation.
+ *
+ * Since a Service can not start an Activity with
+ * {@link Activity#startActivityForResult(Intent, int)} and get a response (only Activities can
+ * handle the results), we have to "Trampoline" this operation by creating an empty Activity whose
+ * only job is to call startActivityForResult with the correct Intent and handle the result.
+ */
+// TODO: SmsManager should be constructed with an activity context so it can start as part of its
+// task and fall back to PickSmsSubscriptionActivity being called in PhoneInterfaceManager if not
+// called from an activity context.
+public class PickSmsSubscriptionActivity extends Activity {
+
+ private static final String LOG_TAG = "PickSmsSubActivity";
+
+ // Defined in Settings SimDialogActivity
+ private static final String RESULT_SUB_ID = "result_sub_id";
+ public static final String DIALOG_TYPE_KEY = "dialog_type";
+ public static final int SMS_PICK_FOR_MESSAGE = 4;
+
+ private static final ComponentName SETTINGS_SUB_PICK_ACTIVITY = new ComponentName(
+ "com.android.settings", "com.android.settings.sim.SimDialogActivity");
+
+ private static final List<IIntegerConsumer> sSmsPickPendingList = new ArrayList<>();
+
+ private static final int REQUEST_GET_SMS_SUB_ID = 1;
+
+ /**
+ * Adds a consumer to the list of pending results that will be accepted once the activity
+ * completes.
+ */
+ public static void addPendingResult(IIntegerConsumer consumer) {
+ synchronized (sSmsPickPendingList) {
+ sSmsPickPendingList.add(consumer);
+ }
+ Log.i(LOG_TAG, "queue pending result, token: " + consumer);
+ }
+
+ private static void sendResultAndClear(int resultId) {
+ // If the calling process died, just ignore callback.
+ synchronized (sSmsPickPendingList) {
+ for (IIntegerConsumer c : sSmsPickPendingList) {
+ try {
+ c.accept(resultId);
+ Log.i(LOG_TAG, "Result received, token: " + c + ", result: " + resultId);
+ } catch (RemoteException e) {
+ // The calling process died, skip this one.
+ }
+ }
+ sSmsPickPendingList.clear();
+ }
+ }
+
+ // Keep track if this activity has been stopped (i.e. user navigated away, power screen off,...)
+ // if so, treat it as the user navigating away and end the task if it is restarted without an
+ // onCreate/onNewIntent.
+ private boolean mPreviouslyStopped = false;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mPreviouslyStopped = false;
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ mPreviouslyStopped = false;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ // This is cause a little jank with the recents display, but there is no other way to handle
+ // the case where activity has stopped and we want to dismiss the dialog. We use the
+ // tag "excludeFromRecents", but in the cases where it is still shown, kill it in onResume.
+ if (mPreviouslyStopped) {
+ finishAndRemoveTask();
+ } else {
+ launchSmsPicker(new Intent(getIntent()));
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ // User navigated away from dialog, send invalid sub id result.
+ mPreviouslyStopped = true;
+ sendResultAndClear(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ // triggers cancelled result for onActivityResult
+ finishActivity(REQUEST_GET_SMS_SUB_ID);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_GET_SMS_SUB_ID) {
+ int result = data == null ? SubscriptionManager.INVALID_SUBSCRIPTION_ID :
+ data.getIntExtra(RESULT_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ if (resultCode == Activity.RESULT_OK) {
+ sendResultAndClear(result);
+ } else {
+ sendResultAndClear(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ }
+ }
+ // This will be handled in onResume - we do not want to call this all the time here because
+ // we need to be able to restart if stopped and a new intent comes in via onNewIntent.
+ if (!mPreviouslyStopped) {
+ finishAndRemoveTask();
+ }
+ }
+
+ private void launchSmsPicker(Intent trampolineIntent) {
+ trampolineIntent.setComponent(SETTINGS_SUB_PICK_ACTIVITY);
+ // Remove this flag if it exists, we want the settings activity to be part of this task.
+ trampolineIntent.removeFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivityForResult(trampolineIntent, REQUEST_GET_SMS_SUB_ID);
+ }
+}
diff --git a/testapps/SmsManagerTestApp/Android.mk b/testapps/SmsManagerTestApp/Android.mk
new file mode 100644
index 0000000..307366b
--- /dev/null
+++ b/testapps/SmsManagerTestApp/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+src_dirs := src
+res_dirs := res
+
+LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
+LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
+
+LOCAL_PACKAGE_NAME := SmsManagerTestApp
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/testapps/SmsManagerTestApp/AndroidManifest.xml b/testapps/SmsManagerTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..c5f4621
--- /dev/null
+++ b/testapps/SmsManagerTestApp/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.phone.testapps.smsmanagertestapp">
+ <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="29" />
+ <uses-permission android:name="android.permission.SEND_SMS"/>
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+ <application android:label="SmsManagerTestApp">
+ <activity
+ android:name=".SmsManagerTestApp"
+ android:label="SmsManagerTestApp">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <service android:name=".SmsManagerTestService" android:exported="false" />
+ <receiver android:name=".SendStatusReceiver"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="com.android.phone.testapps.smsmanagertestapp.message_sent_action" />
+ <data android:scheme="content" />
+ </intent-filter>
+ </receiver>
+
+ </application>
+</manifest>
+
diff --git a/testapps/SmsManagerTestApp/res/layout/activity_main.xml b/testapps/SmsManagerTestApp/res/layout/activity_main.xml
new file mode 100644
index 0000000..39fb6c6
--- /dev/null
+++ b/testapps/SmsManagerTestApp/res/layout/activity_main.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="4dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:layout_weight="0"
+ android:text="Outgoing SMS Phone Number"/>
+
+ <EditText
+ android:id="@+id/phone_number_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="phone" android:text="5555551212"/>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height= "1dp"
+ android:paddingRight="4dp"
+ android:background="?android:attr/listDivider" />
+
+ <Button
+ android:id="@+id/send_text_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingRight="4dp"
+ android:text="@string/send_text_button"/>
+ <Button
+ android:id="@+id/send_text_button_service"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingRight="4dp"
+ android:text="@string/send_text_service_button"/>
+ <Button
+ android:id="@+id/get_sub_for_result_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingRight="4dp"
+ android:text="@string/get_sub_for_result_button"/>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/testapps/SmsManagerTestApp/res/values/donottranslate_strings.xml b/testapps/SmsManagerTestApp/res/values/donottranslate_strings.xml
new file mode 100644
index 0000000..d6497a3
--- /dev/null
+++ b/testapps/SmsManagerTestApp/res/values/donottranslate_strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+
+<resources>
+ <string name="send_text_button">Send Outgoing Text Now.</string>
+ <string name="send_text_service_button">Send Outgoing Text after 5 sec.</string>
+ <string name="get_sub_for_result_button">Ask user for sub id.</string>
+</resources>
\ No newline at end of file
diff --git a/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SendStatusReceiver.java b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SendStatusReceiver.java
new file mode 100644
index 0000000..03709d1
--- /dev/null
+++ b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SendStatusReceiver.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.testapps.smsmanagertestapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.widget.Toast;
+
+/**
+ * Handles the PendingIntent result from SMS messages send to Telephony. Reports the results of
+ * those messages using Toasts.
+ */
+public class SendStatusReceiver extends BroadcastReceiver {
+
+ public static final String MESSAGE_SENT_ACTION =
+ "com.android.phone.testapps.smsmanagertestapp.message_sent_action";
+
+ // Defined by platform, but no constant provided. See docs for SmsManager.sendTextMessage.
+ private static final String EXTRA_ERROR_CODE = "errorCode";
+ private static final String EXTRA_NO_DEFAULT = "noDefault";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int resultCode = getResultCode();
+ if (MESSAGE_SENT_ACTION.equals(intent.getAction())) {
+ int errorCode = intent.getIntExtra(EXTRA_ERROR_CODE, -1);
+ boolean userCancel = intent.getBooleanExtra(EXTRA_NO_DEFAULT, false);
+ if (userCancel) {
+ Toast.makeText(context, "SMS not sent, user cancelled.", Toast.LENGTH_LONG).show();
+ } else {
+ Toast.makeText(context, "SMS result=" + resultCode + ", error extra=" + errorCode,
+ Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+}
diff --git a/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestApp.java b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestApp.java
new file mode 100644
index 0000000..75536f3
--- /dev/null
+++ b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestApp.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.testapps.smsmanagertestapp;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.telephony.SmsManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Supports sending an SMS immediately and offloading the sending of the SMS to a background task.
+ */
+public class SmsManagerTestApp extends Activity {
+
+ private static final int REQUEST_PERMISSION_READ_STATE = 1;
+ private static final int REQUEST_GET_SMS_SUB_ID = 2;
+
+ private static final ComponentName SETTINGS_SUB_PICK_ACTIVITY = new ComponentName(
+ "com.android.settings", "com.android.settings.sim.SimDialogActivity");
+
+ /*
+ * Forwarded constants from SimDialogActivity.
+ */
+ private static final String DIALOG_TYPE_KEY = "dialog_type";
+ public static final String RESULT_SUB_ID = "result_sub_id";
+ private static final int SMS_PICK = 2;
+
+ private static int sMessageId = 0;
+ private boolean mIsReadPhoneStateGranted = false;
+
+ private EditText mPhoneNumber;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_main);
+
+ findViewById(R.id.send_text_button).setOnClickListener(this::sendOutgoingSms);
+ findViewById(R.id.send_text_button_service)
+ .setOnClickListener(this::sendOutgoingSmsService);
+ findViewById(R.id.get_sub_for_result_button).setOnClickListener(this::getSubIdForResult);
+ mPhoneNumber = (EditText) findViewById(R.id.phone_number_text);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (checkSelfPermission(Manifest.permission.READ_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED
+ || checkSelfPermission(Manifest.permission.SEND_SMS)
+ != PackageManager.PERMISSION_GRANTED) {
+ mIsReadPhoneStateGranted = false;
+ requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.SEND_SMS}, REQUEST_PERMISSION_READ_STATE);
+ } else {
+ mIsReadPhoneStateGranted = true;
+ }
+ if (mIsReadPhoneStateGranted) {
+ mPhoneNumber.setText(getPhoneNumber(), TextView.BufferType.NORMAL);
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ stopService(new Intent(this, SmsManagerTestService.class));
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ int[] grantResults) {
+ switch (requestCode) {
+ case REQUEST_PERMISSION_READ_STATE: {
+ if (grantResults.length > 0
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ mIsReadPhoneStateGranted = true;
+ } else {
+ // permission denied
+ Toast.makeText(this, "read_phone_state denied.", Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ }
+
+ if (mIsReadPhoneStateGranted) {
+ mPhoneNumber.setText(getPhoneNumber(), TextView.BufferType.NORMAL);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case (REQUEST_GET_SMS_SUB_ID) : {
+ int resultSubId;
+ if (resultCode == RESULT_OK) {
+ resultSubId = data == null ? -1 : data.getIntExtra(RESULT_SUB_ID,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ Toast.makeText(this, "User picked sub id = " + resultSubId,
+ Toast.LENGTH_LONG).show();
+ } else {
+ Toast.makeText(this, "User cancelled dialog.",
+ Toast.LENGTH_SHORT).show();
+ }
+ break;
+ }
+ }
+ }
+
+
+ private void sendOutgoingSms(View view) {
+ String phoneNumber = mPhoneNumber.getText().toString();
+ if (TextUtils.isEmpty(phoneNumber)) {
+ Toast.makeText(this, "Couldn't get phone number from view! Ignoring request...",
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ if (mIsReadPhoneStateGranted) {
+ SmsManager m = SmsManager.getDefault();
+ m.sendTextMessage(phoneNumber, null, "Test",
+ PendingIntent.getBroadcast(this, sMessageId, getSendStatusIntent(), 0),
+ null);
+ sMessageId++;
+ }
+ }
+
+ private void sendOutgoingSmsService(View view) {
+ String phoneNumber = mPhoneNumber.getText().toString();
+ if (TextUtils.isEmpty(phoneNumber)) {
+ Toast.makeText(this, "Couldn't get phone number from view! Ignoring request...",
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ if (mIsReadPhoneStateGranted) {
+ Intent sendSmsIntent = new Intent(SmsManagerTestService.SEND_SMS);
+ sendSmsIntent.putExtra(SmsManagerTestService.EXTRA_SEND_TEXT, "Text");
+ sendSmsIntent.putExtra(SmsManagerTestService.EXTRA_SEND_NUMBER, phoneNumber);
+ sendSmsIntent.putExtra(SmsManagerTestService.EXTRA_SEND_INTENT,
+ PendingIntent.getBroadcast(this, sMessageId, getSendStatusIntent(), 0));
+ sendSmsIntent.setComponent(new ComponentName(this, SmsManagerTestService.class));
+ startService(sendSmsIntent);
+ sMessageId++;
+ }
+ }
+ private void getSubIdForResult(View view) {
+ // ask the user for a default SMS SIM.
+ Intent intent = new Intent();
+ intent.setComponent(SETTINGS_SUB_PICK_ACTIVITY);
+ intent.putExtra(DIALOG_TYPE_KEY, SMS_PICK);
+ try {
+ startActivity(intent, null);
+ } catch (ActivityNotFoundException anfe) {
+ // If Settings is not installed, only log the error as we do not want to break
+ // legacy applications.
+ Toast.makeText(this, "Unable to launch Settings application.",
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private Intent getSendStatusIntent() {
+ // Encode requestId in intent data
+ return new Intent(SendStatusReceiver.MESSAGE_SENT_ACTION, null, this,
+ SendStatusReceiver.class);
+ }
+
+ private String getPhoneNumber() {
+ String result = "6505551212";
+ int defaultSmsSub = SubscriptionManager.getDefaultSmsSubscriptionId();
+ if (mIsReadPhoneStateGranted) {
+ TelephonyManager tm = getSystemService(TelephonyManager.class);
+ if (tm != null) {
+ tm = tm.createForSubscriptionId(defaultSmsSub);
+ String line1Number = tm.getLine1Number();
+ if (!TextUtils.isEmpty(line1Number)) {
+ return line1Number;
+ }
+ }
+ } else {
+ Toast.makeText(this, "Couldn't resolve line 1 due to permissions error.",
+ Toast.LENGTH_LONG).show();
+ }
+ return result;
+ }
+}
diff --git a/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestService.java b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestService.java
new file mode 100644
index 0000000..fcf4a67
--- /dev/null
+++ b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestService.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.testapps.smsmanagertestapp;
+
+import android.app.IntentService;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.telephony.SmsManager;
+import android.util.Log;
+
+/**
+ * IntentService whose purpose is to handle outgoing SMS intents for this application and schedule
+ * them onto a AsyncTask to sleep for 5 seconds. This allows us to simulate SMS messages being sent
+ * from background services.
+ */
+public class SmsManagerTestService extends IntentService {
+
+ private static final String LOG_TAG = "smsmanagertestservice";
+
+ private static class SendSmsJob extends AsyncTask<Intent, Void, Void> {
+
+ @Override
+ protected Void doInBackground(Intent... intents) {
+ Intent intent = intents[0];
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ // testing
+ }
+
+ String text = intent.getStringExtra(EXTRA_SEND_TEXT);
+ String phoneNumber = intent.getStringExtra(EXTRA_SEND_NUMBER);
+ PendingIntent sendIntent = intent.getParcelableExtra(EXTRA_SEND_INTENT);
+ sendSms(phoneNumber, text, sendIntent);
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void aVoid) {
+ Log.i(LOG_TAG, "SMS sent");
+ }
+
+ }
+
+ public static final String SEND_SMS = "com.android.phone.testapps.smsmanagertestapp.send_sms";
+ public static final String EXTRA_SEND_TEXT = "text";
+ public static final String EXTRA_SEND_NUMBER = "number";
+ public static final String EXTRA_SEND_INTENT = "sendIntent";
+
+ public SmsManagerTestService() {
+ super("SmsManagerTestService");
+ }
+
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ switch (intent.getAction()) {
+ case SEND_SMS : {
+ new SendSmsJob().execute(intent);
+ break;
+ }
+ }
+ }
+
+ private static void sendSms(String phoneNumber, String text, PendingIntent sendIntent) {
+ SmsManager m = SmsManager.getDefault();
+ m.sendTextMessage(phoneNumber, null, text, sendIntent, null);
+ }
+}
diff --git a/tests/src/com/android/phone/CarrierXmlParserTest.java b/tests/src/com/android/phone/CarrierXmlParserTest.java
new file mode 100644
index 0000000..92a8ba7
--- /dev/null
+++ b/tests/src/com/android/phone/CarrierXmlParserTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.Vector;
+
+@RunWith(AndroidJUnit4.class)
+public class CarrierXmlParserTest {
+ private CarrierXmlParser mCarrierXmlParser;
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ @Test
+ public void verifyParserFormat_shouldSameAsXml() {
+ String expected =
+ "((\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*"
+ + "([^*#]*)(\\*([^*#]*))?)?)?)?)?)?)?#)";
+
+ mCarrierXmlParser = new CarrierXmlParser(mContext, -1);
+
+ assertEquals(expected, mCarrierXmlParser.sParserFormat);
+ }
+
+ @Test
+ public void verifyUssdParser_shouldMatchText() {
+ String parserFormat =
+ "((\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*"
+ + "([^*#]*)(\\*([^*#]*))?)?)?)?)?)?)?#)";
+ String text = "120*1*7*123456789*20*1*0*0#";
+ String[] expectedContent = {"120*1*7*123456789*20*1*0*0#",
+ "120*1*7*123456789*20*1*0*0#",
+ "120",
+ "*1*7*123456789*20*1*0*0",
+ "1",
+ "*7*123456789*20*1*0*0",
+ "7",
+ "*123456789*20*1*0*0",
+ "123456789",
+ "*20*1*0*0",
+ "20",
+ "*1*0*0",
+ "1",
+ "*0*0",
+ "0",
+ "*0",
+ "0"};
+ Vector<String> expected = new Vector<>();
+ Collections.addAll(expected, expectedContent);
+
+ CarrierXmlParser.UssdParser ussdParser = new CarrierXmlParser.UssdParser(parserFormat);
+ ussdParser.newFromResponseString(text);
+
+ assertEquals(expected, ussdParser.getResult());
+ }
+}