Merge "Release the N/W request when SIM is removed"
diff --git a/Android.bp b/Android.bp
index a7a533c..e7e78ae 100644
--- a/Android.bp
+++ b/Android.bp
@@ -15,6 +15,10 @@
//
// Mms service
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
android_app {
name: "MmsService",
platform_apis: true,
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index e033528..87e7947 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -25,8 +25,6 @@
<uses-permission android:name="android.permission.WRITE_SMS"/>
<uses-permission android:name="android.permission.BROADCAST_WAP_PUSH"/>
<uses-permission android:name="android.permission.BIND_CARRIER_SERVICES"/>
- <!-- BIND_CARRIER_MESSAGING_SERVICE has been deprecated in favor of BIND_CARRIER_SERVICES. -->
- <uses-permission android:name="android.permission.BIND_CARRIER_MESSAGING_SERVICE"/>
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
<protected-broadcast android:name="android.settings.ENABLE_MMS_DATA_REQUEST"/>
diff --git a/OWNERS b/OWNERS
index e2a657c..cc5e6a3 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,3 +1,7 @@
amitmahajan@google.com
rgreenwalt@google.com
-sanketpadawe@google.com
+tomtaylor@google.com
+afurtado@google.com
+jianxiangp@google.com
+stephshi@google.com
+bellavicevh@google.com
diff --git a/src/com/android/mms/service/ApnSettings.java b/src/com/android/mms/service/ApnSettings.java
index 48383c1..42d6427 100644
--- a/src/com/android/mms/service/ApnSettings.java
+++ b/src/com/android/mms/service/ApnSettings.java
@@ -18,14 +18,13 @@
import android.content.Context;
import android.database.Cursor;
-import android.database.sqlite.SqliteWrapper;
-import android.net.NetworkUtils;
import android.net.Uri;
import android.provider.Telephony;
+import android.telephony.data.ApnSetting;
import android.text.TextUtils;
-import com.android.internal.telephony.PhoneConstants;
import com.android.mms.service.exception.ApnException;
+import com.android.net.module.util.Inet4AddressUtils;
import java.net.URI;
import java.net.URISyntaxException;
@@ -34,6 +33,7 @@
* APN settings used for MMS transactions
*/
public class ApnSettings {
+
// MMSC URL
private final String mServiceCenter;
// MMSC proxy address
@@ -62,29 +62,29 @@
Telephony.Carriers.USER,
Telephony.Carriers.PASSWORD,
};
- private static final int COLUMN_TYPE = 0;
- private static final int COLUMN_MMSC = 1;
- private static final int COLUMN_MMSPROXY = 2;
- private static final int COLUMN_MMSPORT = 3;
- private static final int COLUMN_NAME = 4;
- private static final int COLUMN_APN = 5;
- private static final int COLUMN_BEARER = 6;
- private static final int COLUMN_PROTOCOL = 7;
+ private static final int COLUMN_TYPE = 0;
+ private static final int COLUMN_MMSC = 1;
+ private static final int COLUMN_MMSPROXY = 2;
+ private static final int COLUMN_MMSPORT = 3;
+ private static final int COLUMN_NAME = 4;
+ private static final int COLUMN_APN = 5;
+ private static final int COLUMN_BEARER = 6;
+ private static final int COLUMN_PROTOCOL = 7;
private static final int COLUMN_ROAMING_PROTOCOL = 8;
- private static final int COLUMN_AUTH_TYPE = 9;
- private static final int COLUMN_MVNO_TYPE = 10;
+ private static final int COLUMN_AUTH_TYPE = 9;
+ private static final int COLUMN_MVNO_TYPE = 10;
private static final int COLUMN_MVNO_MATCH_DATA = 11;
- private static final int COLUMN_PROXY = 12;
- private static final int COLUMN_PORT = 13;
- private static final int COLUMN_SERVER = 14;
- private static final int COLUMN_USER = 15;
- private static final int COLUMN_PASSWORD = 16;
+ private static final int COLUMN_PROXY = 12;
+ private static final int COLUMN_PORT = 13;
+ private static final int COLUMN_SERVER = 14;
+ private static final int COLUMN_USER = 15;
+ private static final int COLUMN_PASSWORD = 16;
/**
* Load APN settings from system
- * @param context
- * @param apnName the optional APN name to match
+ *
+ * @param apnName the optional APN name to match
* @param requestId the request ID for logging
*/
public static ApnSettings load(Context context, String apnName, int subId, String requestId)
@@ -98,64 +98,66 @@
if (!TextUtils.isEmpty(apnName)) {
//selection += " AND " + Telephony.Carriers.APN + "=?";
selection = Telephony.Carriers.APN + "=?";
- selectionArgs = new String[]{ apnName };
+ selectionArgs = new String[]{apnName};
}
- Cursor cursor = null;
- try {
- cursor = SqliteWrapper.query(
- context,
- context.getContentResolver(),
- Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "/subId/" + subId),
+
+ try (Cursor cursor = context.getContentResolver().query(
+ Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI, String.valueOf(subId)),
APN_PROJECTION,
selection,
selectionArgs,
- null/*sortOrder*/);
- if (cursor != null) {
- String mmscUrl = null;
- String proxyAddress = null;
- // Default proxy port to 80
- int proxyPort = 80;
- while (cursor.moveToNext()) {
- // Read values from APN settings
- if (isValidApnType(
- cursor.getString(COLUMN_TYPE), PhoneConstants.APN_TYPE_MMS)) {
- mmscUrl = trimWithNullCheck(cursor.getString(COLUMN_MMSC));
- if (TextUtils.isEmpty(mmscUrl)) {
- continue;
- }
- mmscUrl = NetworkUtils.trimV4AddrZeros(mmscUrl);
- try {
- new URI(mmscUrl);
- } catch (URISyntaxException e) {
- throw new ApnException("Invalid MMSC url " + mmscUrl);
- }
- proxyAddress = trimWithNullCheck(cursor.getString(COLUMN_MMSPROXY));
- if (!TextUtils.isEmpty(proxyAddress)) {
- proxyAddress = NetworkUtils.trimV4AddrZeros(proxyAddress);
- final String portString =
- trimWithNullCheck(cursor.getString(COLUMN_MMSPORT));
- if (!TextUtils.isEmpty(portString)) {
- try {
- proxyPort = Integer.parseInt(portString);
- } catch (NumberFormatException e) {
- LogUtil.e(requestId, "Invalid port " + portString + ", use 80");
- }
- }
- }
- return new ApnSettings(
- mmscUrl, proxyAddress, proxyPort, getDebugText(cursor));
- }
- }
+ null/*sortOrder*/)) {
- }
- } finally {
- if (cursor != null) {
- cursor.close();
+ ApnSettings settings = getApnSettingsFromCursor(cursor, requestId);
+ if (settings != null) {
+ return settings;
}
}
throw new ApnException("Can not find valid APN");
}
+ private static ApnSettings getApnSettingsFromCursor(Cursor cursor, String requestId)
+ throws ApnException {
+ if (cursor == null) {
+ return null;
+ }
+
+ // Default proxy port to 80
+ int proxyPort = 80;
+ while (cursor.moveToNext()) {
+ // Read values from APN settings
+ if (isValidApnType(
+ cursor.getString(COLUMN_TYPE), ApnSetting.TYPE_MMS_STRING)) {
+ String mmscUrl = trimWithNullCheck(cursor.getString(COLUMN_MMSC));
+ if (TextUtils.isEmpty(mmscUrl)) {
+ continue;
+ }
+ mmscUrl = Inet4AddressUtils.trimAddressZeros(mmscUrl);
+ try {
+ new URI(mmscUrl);
+ } catch (URISyntaxException e) {
+ throw new ApnException("Invalid MMSC url " + mmscUrl);
+ }
+ String proxyAddress = trimWithNullCheck(cursor.getString(COLUMN_MMSPROXY));
+ if (!TextUtils.isEmpty(proxyAddress)) {
+ proxyAddress = Inet4AddressUtils.trimAddressZeros(proxyAddress);
+ final String portString =
+ trimWithNullCheck(cursor.getString(COLUMN_MMSPORT));
+ if (!TextUtils.isEmpty(portString)) {
+ try {
+ proxyPort = Integer.parseInt(portString);
+ } catch (NumberFormatException e) {
+ LogUtil.e(requestId, "Invalid port " + portString + ", use 80");
+ }
+ }
+ }
+ return new ApnSettings(
+ mmscUrl, proxyAddress, proxyPort, getDebugText(cursor));
+ }
+ }
+ return null;
+ }
+
private static String getDebugText(Cursor cursor) {
final StringBuilder sb = new StringBuilder();
sb.append("APN [");
@@ -183,7 +185,7 @@
mProxyAddress = proxyAddr;
mProxyPort = proxyPort;
mDebugText = debugText;
- }
+ }
public String getMmscUrl() {
return mServiceCenter;
@@ -202,13 +204,13 @@
}
private static boolean isValidApnType(String types, String requestType) {
- // If APN type is unspecified, assume APN_TYPE_ALL.
+ // If APN type is unspecified, assume TYPE_ALL_STRING.
if (TextUtils.isEmpty(types)) {
return true;
}
for (String type : types.split(",")) {
type = type.trim();
- if (type.equals(requestType) || type.equals(PhoneConstants.APN_TYPE_ALL)) {
+ if (type.equals(requestType) || type.equals(ApnSetting.TYPE_ALL_STRING)) {
return true;
}
}
diff --git a/src/com/android/mms/service/DownloadRequest.java b/src/com/android/mms/service/DownloadRequest.java
index 8079fa4..f5fe493 100644
--- a/src/com/android/mms/service/DownloadRequest.java
+++ b/src/com/android/mms/service/DownloadRequest.java
@@ -59,8 +59,8 @@
public DownloadRequest(RequestManager manager, int subId, String locationUrl,
Uri contentUri, PendingIntent downloadedIntent, String creator,
- Bundle configOverrides, Context context) {
- super(manager, subId, creator, configOverrides, context);
+ Bundle configOverrides, Context context, long messageId) {
+ super(manager, subId, creator, configOverrides, context, messageId);
mLocationUrl = locationUrl;
mDownloadedIntent = downloadedIntent;
mContentUri = contentUri;
@@ -72,8 +72,9 @@
final String requestId = getRequestId();
final MmsHttpClient mmsHttpClient = netMgr.getOrCreateHttpClient();
if (mmsHttpClient == null) {
- LogUtil.e(requestId, "MMS network is not ready!");
- throw new MmsHttpException(0/*statusCode*/, "MMS network is not ready");
+ LogUtil.e(requestId, "MMS network is not ready! messageId: " + mMessageId);
+ throw new MmsHttpException(0/*statusCode*/, "MMS network is not ready. messageId: "
+ + mMessageId);
}
return mmsHttpClient.execute(
mLocationUrl,
@@ -106,9 +107,9 @@
if (!mRequestManager.getAutoPersistingPref()) {
return null;
}
- LogUtil.d(requestId, "persistIfRequired");
+ LogUtil.d(requestId, "persistIfRequired. messageId: " + mMessageId);
if (response == null || response.length < 1) {
- LogUtil.e(requestId, "persistIfRequired: empty response");
+ LogUtil.e(requestId, "persistIfRequired: empty response. messageId: " + mMessageId);
return null;
}
final long identity = Binder.clearCallingIdentity();
@@ -117,13 +118,15 @@
mMmsConfig.getBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION);
final GenericPdu pdu = (new PduParser(response, supportMmsContentDisposition)).parse();
if (pdu == null || !(pdu instanceof RetrieveConf)) {
- LogUtil.e(requestId, "persistIfRequired: invalid parsed PDU");
+ LogUtil.e(requestId, "persistIfRequired: invalid parsed PDU. messageId: "
+ + mMessageId);
return null;
}
final RetrieveConf retrieveConf = (RetrieveConf) pdu;
final int status = retrieveConf.getRetrieveStatus();
if (status != PduHeaders.RETRIEVE_STATUS_OK) {
- LogUtil.e(requestId, "persistIfRequired: retrieve failed " + status);
+ LogUtil.e(requestId, "persistIfRequired: retrieve failed " + status
+ + ", messageId: " + mMessageId);
// Update the retrieve status of the NotificationInd
final ContentValues values = new ContentValues(1);
values.put(Telephony.Mms.RETRIEVE_STATUS, status);
@@ -148,7 +151,8 @@
true/*groupMmsEnabled*/,
null/*preOpenedFiles*/);
if (messageUri == null) {
- LogUtil.e(requestId, "persistIfRequired: can not persist message");
+ LogUtil.e(requestId, "persistIfRequired: can not persist message. messageId: "
+ + mMessageId);
return null;
}
// Update some of the properties of the message
@@ -167,7 +171,8 @@
values,
null/*where*/,
null/*selectionArg*/) != 1) {
- LogUtil.e(requestId, "persistIfRequired: can not update message");
+ LogUtil.e(requestId, "persistIfRequired: can not update message. messageId: "
+ + mMessageId);
}
// Delete the corresponding NotificationInd
SqliteWrapper.delete(context,
@@ -181,11 +186,14 @@
return messageUri;
} catch (MmsException e) {
- LogUtil.e(requestId, "persistIfRequired: can not persist message", e);
+ LogUtil.e(requestId, "persistIfRequired: can not persist message. messageId: "
+ + mMessageId, e);
} catch (SQLiteException e) {
- LogUtil.e(requestId, "persistIfRequired: can not update message", e);
+ LogUtil.e(requestId, "persistIfRequired: can not update message. messageId: "
+ + mMessageId, e);
} catch (RuntimeException e) {
- LogUtil.e(requestId, "persistIfRequired: can not parse response", e);
+ LogUtil.e(requestId, "persistIfRequired: can not parse response. messageId: "
+ + mMessageId, e);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -269,29 +277,38 @@
/**
* Downloads the MMS through through the carrier app.
*/
- private final class CarrierDownloadManager extends CarrierMessagingServiceWrapper {
+ private final class CarrierDownloadManager {
// Initialized in downloadMms
private volatile CarrierDownloadCompleteCallback mCarrierDownloadCallback;
+ private final CarrierMessagingServiceWrapper mCarrierMessagingServiceWrapper =
+ new CarrierMessagingServiceWrapper();
+
+ void disposeConnection(Context context) {
+ mCarrierMessagingServiceWrapper.disposeConnection(context);
+ }
void downloadMms(Context context, String carrierMessagingServicePackage,
CarrierDownloadCompleteCallback carrierDownloadCallback) {
mCarrierDownloadCallback = carrierDownloadCallback;
- if (bindToCarrierMessagingService(context, carrierMessagingServicePackage)) {
- LogUtil.v("bindService() for carrier messaging service succeeded");
+ if (mCarrierMessagingServiceWrapper.bindToCarrierMessagingService(
+ context, carrierMessagingServicePackage, ()->onServiceReady())) {
+ LogUtil.v("bindService() for carrier messaging service succeeded. messageId: "
+ + mMessageId);
} else {
- LogUtil.e("bindService() for carrier messaging service failed");
+ LogUtil.e("bindService() for carrier messaging service failed. messageId: "
+ + mMessageId);
carrierDownloadCallback.onDownloadMmsComplete(
CarrierMessagingService.DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK);
}
}
- @Override
- public void onServiceReady() {
+ private void onServiceReady() {
try {
- downloadMms(mContentUri, mSubId, Uri.parse(mLocationUrl),
- mCarrierDownloadCallback);
+ mCarrierMessagingServiceWrapper.downloadMms(
+ mContentUri, mSubId, Uri.parse(mLocationUrl), mCarrierDownloadCallback);
} catch (RuntimeException e) {
- LogUtil.e("Exception downloading MMS using the carrier messaging service: " + e, e);
+ LogUtil.e("Exception downloading MMS for messageId " + mMessageId
+ + " using the carrier messaging service: " + e, e);
mCarrierDownloadCallback.onDownloadMmsComplete(
CarrierMessagingService.DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK);
}
@@ -315,17 +332,19 @@
@Override
public void onSendMmsComplete(int result, byte[] sendConfPdu) {
- LogUtil.e("Unexpected onSendMmsComplete call with result: " + result);
+ LogUtil.e("Unexpected onSendMmsComplete call with result: " + result
+ + ", messageId: " + mMessageId);
}
@Override
public void onDownloadMmsComplete(int result) {
- LogUtil.d("Carrier app result for download: " + result);
+ LogUtil.d("Carrier app result for download: " + result
+ + ", messageId: " + mMessageId);
mCarrierDownloadManager.disposeConnection(mContext);
if (!maybeFallbackToRegularDelivery(result)) {
processResult(mContext, toSmsManagerResult(result), null/* response */,
- 0/* httpStatusCode */);
+ 0/* httpStatusCode */, /* handledByCarrierApp= */ true);
}
}
}
diff --git a/src/com/android/mms/service/MmsConfigManager.java b/src/com/android/mms/service/MmsConfigManager.java
index 20ae9e4..3606ed8 100644
--- a/src/com/android/mms/service/MmsConfigManager.java
+++ b/src/com/android/mms/service/MmsConfigManager.java
@@ -20,9 +20,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.os.BaseBundle;
import android.os.Bundle;
-import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.SmsManager;
import android.telephony.SubscriptionInfo;
@@ -99,90 +97,10 @@
}
/**
- * Filters a bundle to only contain MMS config variables.
- *
- * This is for use with bundles returned by {@link CarrierConfigManager} which contain MMS
- * config and unrelated config. It is assumed that all MMS_CONFIG_* keys are present in the
- * supplied bundle.
- *
- * @param config a Bundle that contains MMS config variables and possibly more.
- * @return a new Bundle that only contains the MMS_CONFIG_* keys defined above.
- * @hide
- */
- private static Bundle getMmsConfig(BaseBundle config) {
- Bundle filtered = new Bundle();
- filtered.putBoolean(SmsManager.MMS_CONFIG_APPEND_TRANSACTION_ID,
- config.getBoolean(SmsManager.MMS_CONFIG_APPEND_TRANSACTION_ID));
- filtered.putBoolean(SmsManager.MMS_CONFIG_MMS_ENABLED,
- config.getBoolean(SmsManager.MMS_CONFIG_MMS_ENABLED));
- filtered.putBoolean(SmsManager.MMS_CONFIG_GROUP_MMS_ENABLED,
- config.getBoolean(SmsManager.MMS_CONFIG_GROUP_MMS_ENABLED));
- filtered.putBoolean(SmsManager.MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED,
- config.getBoolean(SmsManager.MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED));
- filtered.putBoolean(SmsManager.MMS_CONFIG_ALIAS_ENABLED,
- config.getBoolean(SmsManager.MMS_CONFIG_ALIAS_ENABLED));
- filtered.putBoolean(SmsManager.MMS_CONFIG_ALLOW_ATTACH_AUDIO,
- config.getBoolean(SmsManager.MMS_CONFIG_ALLOW_ATTACH_AUDIO));
- filtered.putBoolean(SmsManager.MMS_CONFIG_MULTIPART_SMS_ENABLED,
- config.getBoolean(SmsManager.MMS_CONFIG_MULTIPART_SMS_ENABLED));
- filtered.putBoolean(SmsManager.MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED,
- config.getBoolean(SmsManager.MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED));
- filtered.putBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION,
- config.getBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION));
- filtered.putBoolean(SmsManager.MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES,
- config.getBoolean(SmsManager.MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES));
- filtered.putBoolean(SmsManager.MMS_CONFIG_MMS_READ_REPORT_ENABLED,
- config.getBoolean(SmsManager.MMS_CONFIG_MMS_READ_REPORT_ENABLED));
- filtered.putBoolean(SmsManager.MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED,
- config.getBoolean(SmsManager.MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED));
- filtered.putBoolean(SmsManager.MMS_CONFIG_CLOSE_CONNECTION,
- config.getBoolean(SmsManager.MMS_CONFIG_CLOSE_CONNECTION));
- filtered.putInt(SmsManager.MMS_CONFIG_MAX_MESSAGE_SIZE,
- config.getInt(SmsManager.MMS_CONFIG_MAX_MESSAGE_SIZE));
- filtered.putInt(SmsManager.MMS_CONFIG_MAX_IMAGE_WIDTH,
- config.getInt(SmsManager.MMS_CONFIG_MAX_IMAGE_WIDTH));
- filtered.putInt(SmsManager.MMS_CONFIG_MAX_IMAGE_HEIGHT,
- config.getInt(SmsManager.MMS_CONFIG_MAX_IMAGE_HEIGHT));
- filtered.putInt(SmsManager.MMS_CONFIG_RECIPIENT_LIMIT,
- config.getInt(SmsManager.MMS_CONFIG_RECIPIENT_LIMIT));
- filtered.putInt(SmsManager.MMS_CONFIG_ALIAS_MIN_CHARS,
- config.getInt(SmsManager.MMS_CONFIG_ALIAS_MIN_CHARS));
- filtered.putInt(SmsManager.MMS_CONFIG_ALIAS_MAX_CHARS,
- config.getInt(SmsManager.MMS_CONFIG_ALIAS_MAX_CHARS));
- filtered.putInt(SmsManager.MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD,
- config.getInt(SmsManager.MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD));
- filtered.putInt(SmsManager.MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD,
- config.getInt(SmsManager.MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD));
- filtered.putInt(SmsManager.MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE,
- config.getInt(SmsManager.MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE));
- filtered.putInt(SmsManager.MMS_CONFIG_SUBJECT_MAX_LENGTH,
- config.getInt(SmsManager.MMS_CONFIG_SUBJECT_MAX_LENGTH));
- filtered.putInt(SmsManager.MMS_CONFIG_HTTP_SOCKET_TIMEOUT,
- config.getInt(SmsManager.MMS_CONFIG_HTTP_SOCKET_TIMEOUT));
- filtered.putString(SmsManager.MMS_CONFIG_UA_PROF_TAG_NAME,
- config.getString(SmsManager.MMS_CONFIG_UA_PROF_TAG_NAME));
- filtered.putString(SmsManager.MMS_CONFIG_USER_AGENT,
- config.getString(SmsManager.MMS_CONFIG_USER_AGENT));
- filtered.putString(SmsManager.MMS_CONFIG_UA_PROF_URL,
- config.getString(SmsManager.MMS_CONFIG_UA_PROF_URL));
- filtered.putString(SmsManager.MMS_CONFIG_HTTP_PARAMS,
- config.getString(SmsManager.MMS_CONFIG_HTTP_PARAMS));
- filtered.putString(SmsManager.MMS_CONFIG_EMAIL_GATEWAY_NUMBER,
- config.getString(SmsManager.MMS_CONFIG_EMAIL_GATEWAY_NUMBER));
- filtered.putString(SmsManager.MMS_CONFIG_NAI_SUFFIX,
- config.getString(SmsManager.MMS_CONFIG_NAI_SUFFIX));
- filtered.putBoolean(SmsManager.MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS,
- config.getBoolean(SmsManager.MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS));
- filtered.putBoolean(SmsManager.MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER,
- config.getBoolean(SmsManager.MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER));
- return filtered;
- }
-
- /**
* This loads the MMS config for each active subscription.
*
- * MMS config is fetched from CarrierConfigManager and filtered to only include MMS config
- * variables. The resulting bundles are stored in mSubIdConfigMap.
+ * MMS config is fetched from SmsManager#getCarrierConfigValues(). The resulting bundles are
+ * stored in mSubIdConfigMap.
*/
private void load(Context context) {
List<SubscriptionInfo> subs = mSubscriptionManager.getActiveSubscriptionInfoList();
@@ -193,15 +111,14 @@
// Load all the config bundles into a new map and then swap it with the real map to avoid
// blocking.
final Map<Integer, Bundle> newConfigMap = new ArrayMap<Integer, Bundle>();
- final CarrierConfigManager configManager =
- (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
for (SubscriptionInfo sub : subs) {
final int subId = sub.getSubscriptionId();
LogUtil.i("MmsConfigManager loads mms config for "
+ sub.getMccString() + "/" + sub.getMncString()
+ ", CarrierId " + sub.getCarrierId());
- PersistableBundle config = configManager.getConfigForSubId(subId);
- newConfigMap.put(subId, getMmsConfig(config));
+ newConfigMap.put(
+ subId,
+ SmsManager.getSmsManagerForSubscriptionId(subId).getCarrierConfigValues());
}
synchronized(mSubIdConfigMap) {
mSubIdConfigMap.clear();
diff --git a/src/com/android/mms/service/MmsHttpClient.java b/src/com/android/mms/service/MmsHttpClient.java
index a7d47c4..5cec66f 100644
--- a/src/com/android/mms/service/MmsHttpClient.java
+++ b/src/com/android/mms/service/MmsHttpClient.java
@@ -168,7 +168,7 @@
// Some carriers require that the HTTP connection's socket is closed
// after an MMS request/response is complete. In these cases keep alive
// is disabled. See https://tools.ietf.org/html/rfc7230#section-6.6
- if (mmsConfig.getBoolean(SmsManager.MMS_CONFIG_CLOSE_CONNECTION, false)) {
+ if (mmsConfig.getBoolean(CarrierConfigManager.KEY_MMS_CLOSE_CONNECTION_BOOL, false)) {
LogUtil.i(requestId, "HTTP: Connection close after request");
connection.setRequestProperty(HEADER_CONNECTION, HEADER_CONNECTION_CLOSE);
}
diff --git a/src/com/android/mms/service/MmsNetworkManager.java b/src/com/android/mms/service/MmsNetworkManager.java
index b0eeee8..af4d8a4 100644
--- a/src/com/android/mms/service/MmsNetworkManager.java
+++ b/src/com/android/mms/service/MmsNetworkManager.java
@@ -25,6 +25,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
+import android.net.TelephonyNetworkSpecifier;
import android.os.Handler;
import android.os.Looper;
import android.provider.DeviceConfig;
@@ -183,7 +184,8 @@
mNetworkRequest = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
- .setNetworkSpecifier(Integer.toString(mSubId))
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
+ .setSubscriptionId(mSubId).build())
.build();
mNetworkReleaseTask = new Runnable() {
diff --git a/src/com/android/mms/service/MmsRequest.java b/src/com/android/mms/service/MmsRequest.java
index 9f3e620..f8c882b 100644
--- a/src/com/android/mms/service/MmsRequest.java
+++ b/src/com/android/mms/service/MmsRequest.java
@@ -21,9 +21,19 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.service.carrier.CarrierMessagingService;
-import android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper;
+import android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback;
+import android.telephony.AnomalyReporter;
+import android.telephony.PhoneStateListener;
+import android.telephony.PreciseDataConnectionState;
+import android.telephony.TelephonyCallback;
+import android.telephony.data.ApnSetting;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -32,11 +42,21 @@
import com.android.mms.service.exception.MmsHttpException;
import com.android.mms.service.exception.MmsNetworkException;
+import java.util.UUID;
+
/**
* Base class for MMS requests. This has the common logic of sending/downloading MMS.
*/
public abstract class MmsRequest {
private static final int RETRY_TIMES = 3;
+ // Signal level threshold for both wifi and cellular
+ private static final int SIGNAL_LEVEL_THRESHOLD = 2;
+ // MMS anomaly uuid
+ private final UUID mAnomalyUUID = UUID.fromString("e4330975-17be-43b7-87d6-d9f281d33278");
+ public static final String EXTRA_LAST_CONNECTION_FAILURE_CAUSE_CODE
+ = "android.telephony.extra.LAST_CONNECTION_FAILURE_CAUSE_CODE";
+ public static final String EXTRA_HANDLED_BY_CARRIER_APP
+ = "android.telephony.extra.HANDLED_BY_CARRIER_APP";
/**
* Interface for certain functionalities from MmsService
@@ -83,15 +103,36 @@
protected Bundle mMmsConfigOverrides;
// Context used to get TelephonyManager.
protected Context mContext;
+ protected long mMessageId;
+ protected int mLastConnectionFailure;
+
+ class MonitorTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.PreciseDataConnectionStateListener {
+ @Override
+ public void onPreciseDataConnectionStateChanged(
+ PreciseDataConnectionState connectionState) {
+ if (connectionState == null) {
+ return;
+ }
+ ApnSetting apnSetting = connectionState.getApnSetting();
+ int apnTypes = apnSetting.getApnTypeBitmask();
+ if ((apnTypes & ApnSetting.TYPE_MMS) != 0) {
+ mLastConnectionFailure = connectionState.getLastCauseCode();
+ LogUtil.d("onPreciseDataConnectionStateChanged mLastConnectionFailure: "
+ + mLastConnectionFailure);
+ }
+ }
+ }
public MmsRequest(RequestManager requestManager, int subId, String creator,
- Bundle configOverrides, Context context) {
+ Bundle configOverrides, Context context, long messageId) {
mRequestManager = requestManager;
mSubId = subId;
mCreator = creator;
mMmsConfigOverrides = configOverrides;
mMmsConfig = null;
mContext = context;
+ mMessageId = messageId;
}
public int getSubId() {
@@ -133,7 +174,7 @@
* @param networkManager The network manager to use
*/
public void execute(Context context, MmsNetworkManager networkManager) {
- final String requestId = this.toString();
+ final String requestId = this.getRequestId();
LogUtil.i(requestId, "Executing...");
int result = SmsManager.MMS_ERROR_UNSPECIFIED;
int httpStatusCode = 0;
@@ -150,7 +191,10 @@
long retryDelaySecs = 2;
// Try multiple times of MMS HTTP request, depending on the error.
for (int i = 0; i < RETRY_TIMES; i++) {
+ httpStatusCode = 0; // Clear for retry.
+ MonitorTelephonyCallback connectionStateCallback = new MonitorTelephonyCallback();
try {
+ listenToDataConnectionState(connectionStateCallback);
networkManager.acquireNetwork(requestId);
final String apnName = networkManager.getApnName();
LogUtil.d(requestId, "APN name is " + apnName);
@@ -174,7 +218,9 @@
// Success
break;
} finally {
- networkManager.releaseNetwork(requestId, this instanceof DownloadRequest);
+ // Release the MMS network immediately except successful DownloadRequest.
+ networkManager.releaseNetwork(requestId,
+ this instanceof DownloadRequest && result == Activity.RESULT_OK);
}
} catch (ApnException e) {
LogUtil.e(requestId, "APN failure", e);
@@ -193,6 +239,8 @@
LogUtil.e(requestId, "Unexpected failure", e);
result = SmsManager.MMS_ERROR_UNSPECIFIED;
break;
+ } finally {
+ stopListeningToDataConnectionState(connectionStateCallback);
}
try {
Thread.sleep(retryDelaySecs * 1000, 0/*nano*/);
@@ -200,20 +248,46 @@
retryDelaySecs <<= 1;
}
}
- processResult(context, result, response, httpStatusCode);
+ processResult(context, result, response, httpStatusCode, /* handledByCarrierApp= */ false);
+ }
+
+ private void listenToDataConnectionState(MonitorTelephonyCallback connectionStateCallback) {
+ final TelephonyManager telephonyManager = mContext.getSystemService(
+ TelephonyManager.class).createForSubscriptionId(mSubId);
+ telephonyManager.registerTelephonyCallback(r -> r.run(), connectionStateCallback);
+ }
+
+ private void stopListeningToDataConnectionState(
+ MonitorTelephonyCallback connectionStateCallback) {
+ final TelephonyManager telephonyManager = mContext.getSystemService(
+ TelephonyManager.class).createForSubscriptionId(mSubId);
+ telephonyManager.unregisterTelephonyCallback(connectionStateCallback);
}
/**
* Process the result of the completed request, including updating the message status
* in database and sending back the result via pending intents.
- * @param context The context
+ * @param context The context
* @param result The result code of execution
* @param response The response body
* @param httpStatusCode The optional http status code in case of http failure
+ * @param handledByCarrierApp True if the sending/downloading was handled by a carrier app
+ * rather than MmsService.
*/
- public void processResult(Context context, int result, byte[] response, int httpStatusCode) {
+ public void processResult(Context context, int result, byte[] response, int httpStatusCode,
+ boolean handledByCarrierApp) {
final Uri messageUri = persistIfRequired(context, result, response);
+ final String requestId = this.getRequestId();
+ // As noted in the @param comment above, the httpStatusCode is only set when there's
+ // an http failure. On success, such as an http code of 200, the value here will be 0.
+ // "httpStatusCode: xxx" is now reported for an http failure only.
+ LogUtil.i(requestId, "processResult: "
+ + (result == Activity.RESULT_OK ? "success" : "failure(" + result + ")")
+ + (httpStatusCode != 0 ? ", httpStatusCode: " + httpStatusCode : "")
+ + " handledByCarrierApp: " + handledByCarrierApp
+ + " mLastConnectionFailure: " + mLastConnectionFailure);
+
// Return MMS HTTP request result via PendingIntent
final PendingIntent pendingIntent = getPendingIntent();
if (pendingIntent != null) {
@@ -229,19 +303,97 @@
if (result == SmsManager.MMS_ERROR_HTTP_FAILURE && httpStatusCode != 0) {
fillIn.putExtra(SmsManager.EXTRA_MMS_HTTP_STATUS, httpStatusCode);
}
+ fillIn.putExtra(EXTRA_LAST_CONNECTION_FAILURE_CAUSE_CODE,
+ mLastConnectionFailure);
+ fillIn.putExtra(EXTRA_HANDLED_BY_CARRIER_APP, handledByCarrierApp);
try {
if (!succeeded) {
result = SmsManager.MMS_ERROR_IO_ERROR;
}
+ reportPossibleAnomaly(result, httpStatusCode);
pendingIntent.send(context, result, fillIn);
} catch (PendingIntent.CanceledException e) {
- LogUtil.e(this.toString(), "Sending pending intent canceled", e);
+ LogUtil.e(requestId, "Sending pending intent canceled", e);
}
}
revokeUriPermission(context);
}
+ private void reportPossibleAnomaly(int result, int httpStatusCode) {
+ switch (result) {
+ case SmsManager.MMS_ERROR_HTTP_FAILURE:
+ if (isPoorSignal()) {
+ LogUtil.i(this.toString(), "Poor Signal");
+ break;
+ }
+ case SmsManager.MMS_ERROR_INVALID_APN:
+ case SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS:
+ case SmsManager.MMS_ERROR_UNSPECIFIED:
+ case SmsManager.MMS_ERROR_IO_ERROR:
+ String message = "MMS failed";
+ LogUtil.i(this.toString(),
+ message + " with error: " + result + " httpStatus:" + httpStatusCode);
+ AnomalyReporter.reportAnomaly(generateUUID(result, httpStatusCode), message);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private UUID generateUUID(int result, int httpStatusCode) {
+ long lresult = result;
+ long lhttpStatusCode = httpStatusCode;
+ return new UUID(mAnomalyUUID.getMostSignificantBits(),
+ mAnomalyUUID.getLeastSignificantBits() + ((lhttpStatusCode << 32) + lresult));
+ }
+
+ private boolean isPoorSignal() {
+ // Check Wifi signal strength when IMS registers via Wifi
+ if (isImsOnWifi()) {
+ int rssi = 0;
+ WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
+ final WifiInfo wifiInfo = wifiManager.getConnectionInfo();
+ if (wifiInfo != null) {
+ rssi = wifiInfo.getRssi();
+ } else {
+ return false;
+ }
+ final int wifiLevel = wifiManager.calculateSignalLevel(rssi);
+ LogUtil.d(this.toString(), "Wifi signal rssi: " + rssi + " level:" + wifiLevel);
+ if (wifiLevel <= SIGNAL_LEVEL_THRESHOLD) {
+ return true;
+ }
+ return false;
+ } else {
+ // Check cellular signal strength
+ final TelephonyManager telephonyManager = mContext.getSystemService(
+ TelephonyManager.class).createForSubscriptionId(mSubId);
+ final int cellLevel = telephonyManager.getSignalStrength().getLevel();
+ LogUtil.d(this.toString(), "Cellular signal level:" + cellLevel);
+ if (cellLevel <= SIGNAL_LEVEL_THRESHOLD) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private boolean isImsOnWifi() {
+ ImsMmTelManager imsManager;
+ try {
+ imsManager = ImsMmTelManager.createForSubscriptionId(mSubId);
+ } catch (IllegalArgumentException e) {
+ LogUtil.e(this.toString(), "invalid subid:" + mSubId);
+ return false;
+ }
+ if (imsManager != null) {
+ return imsManager.isAvailable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+ } else {
+ return false;
+ }
+ }
+
/**
* Returns true if sending / downloading using the carrier app has failed and completes the
* action using platform API's, otherwise false.
@@ -251,7 +403,8 @@
== CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK
|| carrierMessagingAppResult
== CarrierMessagingService.DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK) {
- LogUtil.d(this.toString(), "Sending/downloading MMS by IP failed.");
+ LogUtil.d(this.toString(), "Sending/downloading MMS by IP failed. messageId: "
+ + mMessageId);
mRequestManager.addSimRequest(MmsRequest.this);
return true;
} else {
@@ -275,7 +428,8 @@
@Override
public String toString() {
- return getClass().getSimpleName() + '@' + Integer.toHexString(hashCode());
+ return getClass().getSimpleName() + '@' + Integer.toHexString(hashCode())
+ + " messageId: " + mMessageId;
}
@@ -341,20 +495,23 @@
/**
* Base class for handling carrier app send / download result.
*/
- protected abstract class CarrierMmsActionCallback extends CarrierMessagingCallbackWrapper {
+ protected abstract class CarrierMmsActionCallback implements CarrierMessagingCallback {
@Override
public void onSendSmsComplete(int result, int messageRef) {
- LogUtil.e("Unexpected onSendSmsComplete call with result: " + result);
+ LogUtil.e("Unexpected onSendSmsComplete call for messageId " + mMessageId
+ + " with result: " + result);
}
@Override
public void onSendMultipartSmsComplete(int result, int[] messageRefs) {
- LogUtil.e("Unexpected onSendMultipartSmsComplete call with result: " + result);
+ LogUtil.e("Unexpected onSendMultipartSmsComplete call for messageId " + mMessageId
+ + " with result: " + result);
}
@Override
public void onFilterComplete(int result) {
- LogUtil.e("Unexpected onFilterComplete call with result: " + result);
+ LogUtil.e("Unexpected onFilterComplete call for messageId " + mMessageId
+ + " with result: " + result);
}
}
}
diff --git a/src/com/android/mms/service/MmsService.java b/src/com/android/mms/service/MmsService.java
index 66b23ac..eb0fd88 100644
--- a/src/com/android/mms/service/MmsService.java
+++ b/src/com/android/mms/service/MmsService.java
@@ -39,8 +39,10 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Telephony;
+import android.security.NetworkSecurityPolicy;
import android.service.carrier.CarrierMessagingService;
import android.telephony.SmsManager;
+import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
@@ -62,7 +64,10 @@
import java.io.IOException;
import java.util.ArrayDeque;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.Callable;
@@ -70,6 +75,8 @@
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* System service to process MMS API requests
@@ -91,6 +98,15 @@
// The default number of threads allowed to run MMS requests in each queue
public static final int THREAD_POOL_SIZE = 4;
+ /** Represents the received SMS message for importing. */
+ public static final int SMS_TYPE_INCOMING = 0;
+ /** Represents the sent SMS message for importing. */
+ public static final int SMS_TYPE_OUTGOING = 1;
+ /** Message status property: whether the message has been seen. */
+ public static final String MESSAGE_STATUS_SEEN = "seen";
+ /** Message status property: whether the message has been read. */
+ public static final String MESSAGE_STATUS_READ = "read";
+
// Pending requests that are waiting for the SIM to be available
// If a different SIM is currently used by previous requests, the following
// requests will stay in this queue until that SIM finishes its current requests in
@@ -171,8 +187,9 @@
private IMms.Stub mStub = new IMms.Stub() {
@Override
public void sendMessage(int subId, String callingPkg, Uri contentUri,
- String locationUrl, Bundle configOverrides, PendingIntent sentIntent) {
- LogUtil.d("sendMessage");
+ String locationUrl, Bundle configOverrides, PendingIntent sentIntent,
+ long messageId) {
+ LogUtil.d("sendMessage messageId: " + messageId);
enforceSystemUid();
// Make sure the subId is correct
@@ -192,7 +209,8 @@
}
final SendRequest request = new SendRequest(MmsService.this, subId, contentUri,
- locationUrl, sentIntent, callingPkg, configOverrides, MmsService.this);
+ locationUrl, sentIntent, callingPkg, configOverrides, MmsService.this,
+ messageId);
final String carrierMessagingServicePackage =
getCarrierMessagingServicePackageIfExists(subId);
@@ -222,11 +240,12 @@
@Override
public void downloadMessage(int subId, String callingPkg, String locationUrl,
Uri contentUri, Bundle configOverrides,
- PendingIntent downloadedIntent) {
+ PendingIntent downloadedIntent, long messageId) {
// If the subId is no longer active it could be caused by an MVNO using multiple
// subIds, so we should try to download anyway.
// TODO: Fail fast when downloading will fail (i.e. SIM swapped)
- LogUtil.d("downloadMessage: " + MmsHttpClient.redactUrlForNonVerbose(locationUrl));
+ LogUtil.d("downloadMessage: " + MmsHttpClient.redactUrlForNonVerbose(locationUrl) +
+ ", messageId: " + messageId);
enforceSystemUid();
@@ -240,8 +259,27 @@
subId = SubscriptionManager.getDefaultSmsSubscriptionId();
}
+ if (!isActiveSubId(subId)) {
+ List<SubscriptionInfo> activeSubList = getActiveSubscriptionsInGroup(subId);
+ if (activeSubList.isEmpty()) {
+ sendErrorInPendingIntent(downloadedIntent);
+ return;
+ }
+
+ subId = activeSubList.get(0).getSubscriptionId();
+ int defaultSmsSubId = SubscriptionManager.getDefaultSmsSubscriptionId();
+ // If we have default sms subscription, prefer to use that. Otherwise, use first
+ // subscription
+ for (SubscriptionInfo subInfo : activeSubList) {
+ if (subInfo.getSubscriptionId() == defaultSmsSubId) {
+ subId = subInfo.getSubscriptionId();
+ }
+ }
+ }
+
final DownloadRequest request = new DownloadRequest(MmsService.this, subId, locationUrl,
- contentUri, downloadedIntent, callingPkg, configOverrides, MmsService.this);
+ contentUri, downloadedIntent, callingPkg, configOverrides, MmsService.this,
+ messageId);
final String carrierMessagingServicePackage =
getCarrierMessagingServicePackageIfExists(subId);
@@ -262,22 +300,46 @@
addSimRequest(request);
}
- @Override
- public Bundle getCarrierConfigValues(int subId) {
- LogUtil.d("getCarrierConfigValues");
- // Make sure the subId is correct
- if (!SubscriptionManager.isValidSubscriptionId(subId)) {
- LogUtil.e("Invalid subId " + subId);
- return new Bundle();
+ private List<SubscriptionInfo> getActiveSubscriptionsInGroup(int subId) {
+ SubscriptionManager subManager =
+ (SubscriptionManager) getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+
+ if (subManager == null) {
+ return Collections.emptyList();
}
- if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
- subId = SubscriptionManager.getDefaultSmsSubscriptionId();
+
+ List<SubscriptionInfo> subList = subManager.getAvailableSubscriptionInfoList();
+
+ if (subList == null) {
+ return Collections.emptyList();
}
- final Bundle mmsConfig = MmsConfigManager.getInstance().getMmsConfigBySubId(subId);
- if (mmsConfig == null) {
- return new Bundle();
+
+ SubscriptionInfo subscriptionInfo = null;
+ for (SubscriptionInfo subInfo : subList) {
+ if (subInfo.getSubscriptionId() == subId) {
+ subscriptionInfo = subInfo;
+ break;
+ }
}
- return mmsConfig;
+
+ if (subscriptionInfo == null) {
+ return Collections.emptyList();
+ }
+
+ if (subscriptionInfo.getGroupUuid() == null) {
+ return Collections.emptyList();
+ }
+
+ List<SubscriptionInfo> subscriptionInGroupList =
+ subManager.getSubscriptionsInGroup(subscriptionInfo.getGroupUuid());
+
+ // the list is sorted by isOpportunistic and isOpportunistic == false will have higher
+ // priority
+ return subscriptionInGroupList.stream()
+ .filter(info ->
+ info.getSimSlotIndex() != SubscriptionManager.INVALID_SIM_SLOT_INDEX)
+ .sorted(Comparator.comparing(SubscriptionInfo::isOpportunistic))
+ .collect(Collectors.toList());
}
@Override
@@ -555,6 +617,9 @@
LogUtil.d("onCreate");
// Load mms_config
MmsConfigManager.getInstance().init(this);
+
+ NetworkSecurityPolicy.getInstance().setCleartextTrafficPermitted(true);
+
// Initialize running request state
for (int i = 0; i < mRunningRequestExecutors.length; i++) {
mRunningRequestExecutors[i] = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
@@ -578,11 +643,11 @@
boolean seen, boolean read, String creator) {
Uri insertUri = null;
switch (type) {
- case SmsManager.SMS_TYPE_INCOMING:
+ case SMS_TYPE_INCOMING:
insertUri = Telephony.Sms.Inbox.CONTENT_URI;
break;
- case SmsManager.SMS_TYPE_OUTGOING:
+ case SMS_TYPE_OUTGOING:
insertUri = Telephony.Sms.Sent.CONTENT_URI;
break;
}
@@ -702,14 +767,14 @@
return false;
}
final ContentValues values = new ContentValues();
- if (statusValues.containsKey(SmsManager.MESSAGE_STATUS_READ)) {
- final Integer val = statusValues.getAsInteger(SmsManager.MESSAGE_STATUS_READ);
+ if (statusValues.containsKey(MESSAGE_STATUS_READ)) {
+ final Integer val = statusValues.getAsInteger(MESSAGE_STATUS_READ);
if (val != null) {
// MMS uses the same column name
values.put(Telephony.Sms.READ, val);
}
- } else if (statusValues.containsKey(SmsManager.MESSAGE_STATUS_SEEN)) {
- final Integer val = statusValues.getAsInteger(SmsManager.MESSAGE_STATUS_SEEN);
+ } else if (statusValues.containsKey(MESSAGE_STATUS_SEEN)) {
+ final Integer val = statusValues.getAsInteger(MESSAGE_STATUS_SEEN);
if (val != null) {
// MMS uses the same column name
values.put(Telephony.Sms.SEEN, val);
diff --git a/src/com/android/mms/service/SendRequest.java b/src/com/android/mms/service/SendRequest.java
index fefbbc0..8fd5185 100644
--- a/src/com/android/mms/service/SendRequest.java
+++ b/src/com/android/mms/service/SendRequest.java
@@ -57,8 +57,9 @@
private final PendingIntent mSentIntent;
public SendRequest(RequestManager manager, int subId, Uri contentUri, String locationUrl,
- PendingIntent sentIntent, String creator, Bundle configOverrides, Context context) {
- super(manager, subId, creator, configOverrides, context);
+ PendingIntent sentIntent, String creator, Bundle configOverrides, Context context,
+ long messageId) {
+ super(manager, subId, creator, configOverrides, context, messageId);
mPduUri = contentUri;
mPduData = null;
mLocationUrl = locationUrl;
@@ -71,8 +72,9 @@
final String requestId = getRequestId();
final MmsHttpClient mmsHttpClient = netMgr.getOrCreateHttpClient();
if (mmsHttpClient == null) {
- LogUtil.e(requestId, "MMS network is not ready!");
- throw new MmsHttpException(0/*statusCode*/, "MMS network is not ready");
+ String notReady = "MMS network is not ready! messageId: " + mMessageId;
+ LogUtil.e(requestId, notReady);
+ throw new MmsHttpException(0/*statusCode*/, notReady);
}
final GenericPdu parsedPdu = parsePdu();
notifyIfEmergencyContactNoThrow(parsedPdu);
@@ -93,14 +95,14 @@
final String requestId = getRequestId();
try {
if (mPduData == null) {
- LogUtil.w(requestId, "Empty PDU raw data");
+ LogUtil.w(requestId, "Empty PDU raw data. messageId: " + mMessageId);
return null;
}
final boolean supportContentDisposition =
mMmsConfig.getBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION);
return new PduParser(mPduData, supportContentDisposition).parse();
} catch (final Exception e) {
- LogUtil.w(requestId, "Failed to parse PDU raw data");
+ LogUtil.w(requestId, "Failed to parse PDU raw data. messageId: " + mMessageId);
}
return null;
}
@@ -113,7 +115,7 @@
try {
notifyIfEmergencyContact(parsedPdu);
} catch (Exception e) {
- LogUtil.w(getRequestId(), "Error in notifyIfEmergencyContact", e);
+ LogUtil.w(getRequestId(), "Error in notifyIfEmergencyContact. messageId: " + mMessageId, e);
}
}
@@ -122,15 +124,18 @@
SendReq sendReq = (SendReq) parsedPdu;
for (EncodedStringValue encodedStringValue : sendReq.getTo()) {
if (isEmergencyNumber(encodedStringValue.getString())) {
- LogUtil.i(getRequestId(), "Notifying emergency contact");
+ LogUtil.i(getRequestId(), "Notifying emergency contact. messageId: "
+ + mMessageId);
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
try {
- notifyEmergencyContact(mContext);
+ BlockedNumberContract.SystemContract
+ .notifyEmergencyContact(mContext);
} catch (Exception e) {
LogUtil.e(getRequestId(),
- "Exception notifying emergency contact: " + e);
+ "Exception notifying emergency contact. messageId: "
+ + mMessageId + e);
}
return null;
}
@@ -167,9 +172,9 @@
// Not required to persist
return null;
}
- LogUtil.d(requestId, "persistIfRequired");
+ LogUtil.d(requestId, "persistIfRequired. messageId: " + mMessageId);
if (mPduData == null) {
- LogUtil.e(requestId, "persistIfRequired: empty PDU");
+ LogUtil.e(requestId, "persistIfRequired: empty PDU. messageId: " + mMessageId);
return null;
}
final long identity = Binder.clearCallingIdentity();
@@ -179,11 +184,12 @@
// Persist the request PDU first
GenericPdu pdu = (new PduParser(mPduData, supportContentDisposition)).parse();
if (pdu == null) {
- LogUtil.e(requestId, "persistIfRequired: can't parse input PDU");
+ LogUtil.e(requestId, "persistIfRequired: can't parse input PDU. messageId: "
+ + mMessageId);
return null;
}
if (!(pdu instanceof SendReq)) {
- LogUtil.d(requestId, "persistIfRequired: not SendReq");
+ LogUtil.d(requestId, "persistIfRequired: not SendReq. messageId: " + mMessageId);
return null;
}
final PduPersister persister = PduPersister.getPduPersister(context);
@@ -194,7 +200,8 @@
true/*groupMmsEnabled*/,
null/*preOpenedFiles*/);
if (messageUri == null) {
- LogUtil.e(requestId, "persistIfRequired: can not persist message");
+ LogUtil.e(requestId, "persistIfRequired: can not persist message. messageId: "
+ + mMessageId);
return null;
}
// Update the additional columns based on the send result
@@ -232,13 +239,16 @@
values.put(Telephony.Mms.SUBSCRIPTION_ID, mSubId);
if (SqliteWrapper.update(context, context.getContentResolver(), messageUri, values,
null/*where*/, null/*selectionArg*/) != 1) {
- LogUtil.e(requestId, "persistIfRequired: failed to update message");
+ LogUtil.e(requestId, "persistIfRequired: failed to update message. messageId: "
+ + mMessageId);
}
return messageUri;
} catch (MmsException e) {
- LogUtil.e(requestId, "persistIfRequired: can not persist message", e);
+ LogUtil.e(requestId, "persistIfRequired: can not persist message. messageId: "
+ + mMessageId, e);
} catch (RuntimeException e) {
- LogUtil.e(requestId, "persistIfRequired: unexpected parsing failure", e);
+ LogUtil.e(requestId, "persistIfRequired: unexpected parsing failure. messageId: "
+ + mMessageId, e);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -253,11 +263,12 @@
private void updateDestinationAddress(final GenericPdu pdu) {
final String requestId = getRequestId();
if (pdu == null) {
- LogUtil.e(requestId, "updateDestinationAddress: can't parse input PDU");
+ LogUtil.e(requestId, "updateDestinationAddress: can't parse input PDU. messageId: "
+ + mMessageId);
return ;
}
if (!(pdu instanceof SendReq)) {
- LogUtil.i(requestId, "updateDestinationAddress: not SendReq");
+ LogUtil.i(requestId, "updateDestinationAddress: not SendReq. messageId: " + mMessageId);
return;
}
@@ -382,33 +393,43 @@
/**
* Sends the MMS through through the carrier app.
*/
- private final class CarrierSendManager extends CarrierMessagingServiceWrapper {
+ private final class CarrierSendManager {
// Initialized in sendMms
private volatile CarrierSendCompleteCallback mCarrierSendCompleteCallback;
+ private final CarrierMessagingServiceWrapper mCarrierMessagingServiceWrapper =
+ new CarrierMessagingServiceWrapper();
+
+ void disposeConnection(Context context) {
+ mCarrierMessagingServiceWrapper.disposeConnection(context);
+ }
void sendMms(Context context, String carrierMessagingServicePackage,
CarrierSendCompleteCallback carrierSendCompleteCallback) {
mCarrierSendCompleteCallback = carrierSendCompleteCallback;
- if (bindToCarrierMessagingService(context, carrierMessagingServicePackage)) {
- LogUtil.v("bindService() for carrier messaging service succeeded");
+ if (mCarrierMessagingServiceWrapper.bindToCarrierMessagingService(
+ context, carrierMessagingServicePackage, () -> onServiceReady())) {
+ LogUtil.v("bindService() for carrier messaging service succeeded. messageId: "
+ + mMessageId);
} else {
- LogUtil.e("bindService() for carrier messaging service failed");
+ LogUtil.e("bindService() for carrier messaging service failed. messageId: "
+ + mMessageId);
carrierSendCompleteCallback.onSendMmsComplete(
CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
null /* no sendConfPdu */);
}
}
- @Override
- public void onServiceReady() {
+ private void onServiceReady() {
try {
Uri locationUri = null;
if (mLocationUrl != null) {
locationUri = Uri.parse(mLocationUrl);
}
- sendMms(mPduUri, mSubId, locationUri, mCarrierSendCompleteCallback);
+ mCarrierMessagingServiceWrapper.sendMms(
+ mPduUri, mSubId, locationUri, mCarrierSendCompleteCallback);
} catch (RuntimeException e) {
- LogUtil.e("Exception sending MMS using the carrier messaging service: " + e, e);
+ LogUtil.e("Exception sending MMS using the carrier messaging service. messageId: "
+ + mMessageId + e, e);
mCarrierSendCompleteCallback.onSendMmsComplete(
CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
null /* no sendConfPdu */);
@@ -432,35 +453,19 @@
@Override
public void onSendMmsComplete(int result, byte[] sendConfPdu) {
- LogUtil.d("Carrier app result for send: " + result);
+ LogUtil.d("Carrier app result for sending messageId " + mMessageId + ": " + result);
mCarrierSendManager.disposeConnection(mContext);
if (!maybeFallbackToRegularDelivery(result)) {
processResult(mContext, toSmsManagerResult(result), sendConfPdu,
- 0/* httpStatusCode */);
+ 0/* httpStatusCode */, /* handledByCarrierApp= */ true);
}
}
@Override
public void onDownloadMmsComplete(int result) {
- LogUtil.e("Unexpected onDownloadMmsComplete call with result: " + result);
- }
- }
-
- /**
- * Notifies the provider that emergency services were contacted by the user.
- */
- private void notifyEmergencyContact(Context context) {
- try {
- LogUtil.i("notifyEmergencyContact; caller=%s", context.getOpPackageName());
- context.getContentResolver().call(
- BlockedNumberContract.AUTHORITY_URI,
- BlockedNumberContract.METHOD_NOTIFY_EMERGENCY_CONTACT,
- null, null);
- } catch (NullPointerException | IllegalArgumentException ex) {
- // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
- // either of these happen.
- LogUtil.w(null, "notifyEmergencyContact: provider not ready.");
+ LogUtil.e("Unexpected onDownloadMmsComplete call for messageId " + mMessageId
+ + " with result: " + result);
}
}
}
diff --git a/tests/robotests/Android.bp b/tests/robotests/Android.bp
index e983542..1b170c0 100644
--- a/tests/robotests/Android.bp
+++ b/tests/robotests/Android.bp
@@ -2,6 +2,10 @@
// MmsService Robolectric test target. #
//############################################################
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
android_robolectric_test {
name: "MmsServiceRoboTests",
@@ -9,10 +13,10 @@
java_resource_dirs: ["config"],
- static_libs: ["testng"],
+ static_libs: [
+ "androidx.test.core",
+ "testng",
+ ],
instrumentation_for: "MmsService",
}
-
-
-
diff --git a/tests/robotests/src/com/android/mms/service/ApnSettingsTest.java b/tests/robotests/src/com/android/mms/service/ApnSettingsTest.java
new file mode 100644
index 0000000..b184e16
--- /dev/null
+++ b/tests/robotests/src/com/android/mms/service/ApnSettingsTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.mms.service;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Telephony;
+import android.telephony.data.ApnSetting;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.mms.service.exception.ApnException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.fakes.RoboCursor;
+import org.robolectric.shadows.ShadowContentResolver;
+
+import java.util.Arrays;
+
+@RunWith(RobolectricTestRunner.class)
+public final class ApnSettingsTest {
+
+ private Context context;
+
+ @Before
+ public void setUp() {
+ context = ApplicationProvider.getApplicationContext();
+ }
+
+ @Test
+ public void load_noApnSettings_throwsApnException() {
+ assertThrows(ApnException.class,
+ () -> ApnSettings.load(context, "apnName", /* subId= */ 0, "requestId"));
+ }
+
+ @Test
+ public void getApnSettingsFromCursor_validSettings_correctApnSettingsLoaded() throws Exception {
+ createApnSettingsCursor("mmscUrl", "mmsProxy", /* proxyPort= */ "123");
+ ApnSettings apnSettings = ApnSettings.load(context, "apnName", /* subId= */ 0, "requestId");
+
+ assertThat(apnSettings.getProxyPort()).isEqualTo(123);
+ assertThat(apnSettings.getMmscUrl()).isEqualTo("mmscUrl");
+ assertThat(apnSettings.getProxyAddress()).isEqualTo("mmsProxy");
+ }
+
+ @Test
+ public void getApnSettingsFromCursor_nullMmsPort_defaultProxyPortUsed() throws Exception {
+ createApnSettingsCursor("mmscUrl", "mmsProxy", /* proxyPort= */ null);
+ ApnSettings apnSettings = ApnSettings.load(context, "apnName", /* subId= */ 0, "requestId");
+ assertThat(apnSettings.getProxyPort()).isEqualTo(80);
+ }
+
+ @Test
+ public void getApnSettingsFromCursor_emptyMmsPort_defaultProxyPortUsed() throws Exception {
+ createApnSettingsCursor("mmscUrl", "mmsProxy",
+ /* proxyPort= */ "");
+ ApnSettings apnSettings = ApnSettings.load(context, "apnName", /* subId= */ 0, "requestId");
+ assertThat(apnSettings.getProxyPort()).isEqualTo(80);
+ }
+
+ private void createApnSettingsCursor(String mmscUrl, String mmsProxy, String proxyPort) {
+ Object[][] apnValues =
+ {new Object[]{ApnSetting.TYPE_MMS_STRING, mmscUrl, mmsProxy, proxyPort}};
+ RoboCursor cursor = new RoboCursor();
+ cursor.setResults(apnValues);
+ cursor.setColumnNames(Arrays.asList(Telephony.Carriers.TYPE, Telephony.Carriers.MMSC,
+ Telephony.Carriers.MMSPROXY, Telephony.Carriers.MMSPORT));
+
+ ShadowContentResolver.registerProviderInternal(
+ Telephony.Carriers.CONTENT_URI.getAuthority(), new FakeApnSettingsProvider(cursor));
+ }
+
+ private final class FakeApnSettingsProvider extends ContentProvider {
+
+ private final Cursor cursor;
+
+ FakeApnSettingsProvider(Cursor cursor) {
+ this.cursor = cursor;
+ }
+
+ @Override
+ public boolean onCreate() {
+ return false;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection,
+ String selection, String[] selectionArgs, String sortOrder) {
+ return cursor;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+ }
+
+}
diff --git a/tests/robotests/src/com/android/mms/service/MmsServiceRoboTest.java b/tests/robotests/src/com/android/mms/service/MmsServiceRoboTest.java
index 5e4c309..c43fa74 100644
--- a/tests/robotests/src/com/android/mms/service/MmsServiceRoboTest.java
+++ b/tests/robotests/src/com/android/mms/service/MmsServiceRoboTest.java
@@ -56,13 +56,15 @@
public void testSendMessage_DoesNotThrowIfSystemUid() throws RemoteException {
ShadowBinder.setCallingUid(Process.SYSTEM_UID);
binder.sendMessage(/* subId= */ 0, "callingPkg", Uri.parse("contentUri"),
- "locationUrl", /* configOverrides= */ null, /* sentIntent= */ null);
+ "locationUrl", /* configOverrides= */ null, /* sentIntent= */ null,
+ /* messageId= */ 0L);
}
@Test
public void testSendMessageThrows_IfNotSystemUid() {
assertThrows(SecurityException.class,
() -> binder.sendMessage(/* subId= */ 0, "callingPkg", Uri.parse("contentUri"),
- "locationUrl", /* configOverrides= */ null, /* sentIntent= */ null));
+ "locationUrl", /* configOverrides= */ null, /* sentIntent= */ null,
+ /* messageId= */ 0L));
}
}