• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.mms.service;
18 
19 import android.annotation.NonNull;
20 import android.app.Activity;
21 import android.app.PendingIntent;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.net.Uri;
25 import android.net.wifi.WifiInfo;
26 import android.net.wifi.WifiManager;
27 import android.os.Bundle;
28 import android.service.carrier.CarrierMessagingService;
29 import android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback;
30 import android.telephony.AnomalyReporter;
31 import android.telephony.CarrierConfigManager;
32 import android.telephony.PreciseDataConnectionState;
33 import android.telephony.ServiceState;
34 import android.telephony.SmsManager;
35 import android.telephony.TelephonyCallback;
36 import android.telephony.TelephonyManager;
37 import android.telephony.data.ApnSetting;
38 import android.telephony.ims.ImsMmTelManager;
39 import android.telephony.ims.feature.MmTelFeature;
40 import android.telephony.ims.stub.ImsRegistrationImplBase;
41 import android.util.SparseArray;
42 
43 import com.android.internal.annotations.GuardedBy;
44 import com.android.internal.annotations.VisibleForTesting;
45 import com.android.internal.telephony.flags.Flags;
46 import com.android.mms.service.exception.ApnException;
47 import com.android.mms.service.exception.MmsHttpException;
48 import com.android.mms.service.exception.MmsNetworkException;
49 import com.android.mms.service.metrics.MmsStats;
50 
51 import java.util.UUID;
52 import java.util.concurrent.CountDownLatch;
53 import java.util.concurrent.TimeUnit;
54 
55 /**
56  * Base class for MMS requests. This has the common logic of sending/downloading MMS.
57  */
58 public abstract class MmsRequest {
59     private static final int RETRY_TIMES = 3;
60     // Signal level threshold for both wifi and cellular
61     private static final int SIGNAL_LEVEL_THRESHOLD = 2;
62     public static final String EXTRA_LAST_CONNECTION_FAILURE_CAUSE_CODE
63             = "android.telephony.extra.LAST_CONNECTION_FAILURE_CAUSE_CODE";
64     public static final String EXTRA_HANDLED_BY_CARRIER_APP
65             = "android.telephony.extra.HANDLED_BY_CARRIER_APP";
66 
67     /**
68      * Interface for certain functionalities from MmsService
69      */
70     public static interface RequestManager {
71         /**
72          * Enqueue an MMS request
73          *
74          * @param request the request to enqueue
75          */
addSimRequest(MmsRequest request)76         public void addSimRequest(MmsRequest request);
77 
78         /*
79          * @return Whether to auto persist received MMS
80          */
getAutoPersistingPref()81         public boolean getAutoPersistingPref();
82 
83         /**
84          * Read pdu (up to maxSize bytes) from supplied content uri
85          * @param contentUri content uri from which to read
86          * @param maxSize maximum number of bytes to read
87          * @param callingUser user id of the calling app
88          * @return read pdu (else null in case of error or too big)
89          */
readPduFromContentUri(final Uri contentUri, final int maxSize, int callingUser)90         public byte[] readPduFromContentUri(final Uri contentUri, final int maxSize,
91                 int callingUser);
92 
93         /**
94          * Write pdu to supplied content uri
95          * @param contentUri content uri to which bytes should be written
96          * @param pdu pdu bytes to write
97          * @return true in case of success (else false)
98          */
writePduToContentUri(final Uri contentUri, final byte[] pdu)99         public boolean writePduToContentUri(final Uri contentUri, final byte[] pdu);
100     }
101 
102     // The reference to the pending requests manager (i.e. the MmsService)
103     protected RequestManager mRequestManager;
104     // The SIM id
105     protected int mSubId;
106     // The creator app
107     protected String mCreator;
108     // MMS config
109     protected Bundle mMmsConfig;
110     // Context used to get TelephonyManager.
111     protected Context mContext;
112     protected long mMessageId;
113     protected int mLastConnectionFailure;
114     private MmsStats mMmsStats;
115     private int result;
116     private int httpStatusCode;
117     protected TelephonyManager mTelephonyManager;
118     @VisibleForTesting
119     public int SATELLITE_MMS_SIZE_LIMIT = 3 * 1024;    // TODO - read from a carrier config setting
120 
121     protected enum MmsRequestState {
122         Unknown,
123         Created,
124         PrepareForHttpRequest,
125         AcquiringNetwork,
126         LoadingApn,
127         DoingHttp,
128         Success,
129         Failure
130     };
131     protected MmsRequestState currentState = MmsRequestState.Unknown;
132 
133     class MonitorTelephonyCallback extends TelephonyCallback implements
134             TelephonyCallback.PreciseDataConnectionStateListener {
135 
136         /** The lock to update mNetworkIdToApn. */
137         private final Object mLock = new Object();
138         /**
139          * Track the network agent Id to APN. Usually we have at most 2 networks that are capable of
140          * MMS at the same time (terrestrial and satellite)
141          */
142         @GuardedBy("mLock")
143         private final SparseArray<ApnSetting> mNetworkIdToApn = new SparseArray<>(2);
144         @Override
onPreciseDataConnectionStateChanged( @onNull PreciseDataConnectionState connectionState)145         public void onPreciseDataConnectionStateChanged(
146                 @NonNull PreciseDataConnectionState connectionState) {
147             ApnSetting apnSetting = connectionState.getApnSetting();
148             if (apnSetting != null) {
149                 // Only track networks that are capable of MMS.
150                 if ((apnSetting.getApnTypeBitmask() & ApnSetting.TYPE_MMS) != 0) {
151                     LogUtil.d("onPreciseDataConnectionStateChanged: " + connectionState);
152                     mLastConnectionFailure = connectionState.getLastCauseCode();
153                     if (Flags.mmsGetApnFromPdsc()) {
154                         synchronized (mLock) {
155                             mNetworkIdToApn.put(connectionState.getNetId(), apnSetting);
156                         }
157                     }
158                 }
159             }
160         }
161     }
162 
MmsRequest(RequestManager requestManager, int subId, String creator, Bundle mmsConfig, Context context, long messageId, MmsStats mmsStats, TelephonyManager telephonyManager)163     public MmsRequest(RequestManager requestManager, int subId, String creator,
164             Bundle mmsConfig, Context context, long messageId, MmsStats mmsStats,
165             TelephonyManager telephonyManager) {
166         currentState = MmsRequestState.Created;
167         mRequestManager = requestManager;
168         mSubId = subId;
169         mCreator = creator;
170         mMmsConfig = mmsConfig;
171         mContext = context;
172         mMessageId = messageId;
173         mMmsStats = mmsStats;
174         mTelephonyManager = telephonyManager;
175     }
176 
getSubId()177     public int getSubId() {
178         return mSubId;
179     }
180 
181     /**
182      * Execute the request
183      *
184      * @param context The context
185      * @param networkManager The network manager to use
186      */
execute(Context context, MmsNetworkManager networkManager)187     public void execute(Context context, MmsNetworkManager networkManager) {
188         final String requestId = this.getRequestId();
189         LogUtil.i(requestId, "Executing...");
190         result = SmsManager.MMS_ERROR_UNSPECIFIED;
191         httpStatusCode = 0;
192         byte[] response = null;
193         int retryId = 0;
194         currentState = MmsRequestState.PrepareForHttpRequest;
195 
196         if (!prepareForHttpRequest()) { // Prepare request, like reading pdu data from user
197             LogUtil.e(requestId, "Failed to prepare for request");
198             result = SmsManager.MMS_ERROR_IO_ERROR;
199         } else { // Execute
200             long retryDelaySecs = 2;
201             // Try multiple times of MMS HTTP request, depending on the error.
202             for (retryId = 0; retryId < RETRY_TIMES; retryId++) {
203                 httpStatusCode = 0; // Clear for retry.
204                 MonitorTelephonyCallback connectionStateCallback = new MonitorTelephonyCallback();
205                 try {
206                     listenToDataConnectionState(connectionStateCallback);
207                     currentState = MmsRequestState.AcquiringNetwork;
208                     int networkId = networkManager.acquireNetwork(requestId);
209                     currentState = MmsRequestState.LoadingApn;
210                     ApnSettings apn = null;
211                     ApnSetting networkApn = null;
212                     if (Flags.mmsGetApnFromPdsc()) {
213                         synchronized (connectionStateCallback.mLock) {
214                             networkApn = connectionStateCallback.mNetworkIdToApn.get(networkId);
215                         }
216                         if (networkApn != null) {
217                             apn = ApnSettings.getApnSettingsFromNetworkApn(networkApn);
218                         }
219                     }
220                     if (apn == null) {
221                         final String apnName = networkManager.getApnName();
222                         LogUtil.d(requestId, "APN name is " + apnName);
223                         try {
224                             apn = ApnSettings.load(context, apnName, mSubId, requestId);
225                         } catch (ApnException e) {
226                             // If no APN could be found, fall back to trying without the APN name
227                             if (apnName == null) {
228                                 // If the APN name was already null then don't need to retry
229                                 throw (e);
230                             }
231                             LogUtil.i(requestId, "No match with APN name: "
232                                     + apnName + ", try with no name");
233                             apn = ApnSettings.load(context, null, mSubId, requestId);
234                         }
235                     }
236 
237                     if (Flags.mmsGetApnFromPdsc() && networkApn == null && apn != null) {
238                         reportAnomaly("Can't find MMS APN in mms network",
239                                 UUID.fromString("2bdda74d-3cf4-44ad-a87f-24c961212a6f"));
240                     }
241 
242                     LogUtil.d(requestId, "Using APN " + apn);
243                     if (networkManager.isSatelliteTransport()
244                             && !canTransferPayloadOnCurrentNetwork()) {
245                         LogUtil.e(requestId, "PDU too large for satellite");
246                         result = SmsManager.MMS_ERROR_TOO_LARGE_FOR_TRANSPORT;
247                         break;
248                     }
249                     currentState = MmsRequestState.DoingHttp;
250                     response = doHttp(context, networkManager, apn);
251                     result = Activity.RESULT_OK;
252                     // Success
253                     break;
254                 } catch (ApnException e) {
255                     LogUtil.e(requestId, "APN failure", e);
256                     result = SmsManager.MMS_ERROR_INVALID_APN;
257                     break;
258                 } catch (MmsNetworkException e) {
259                     LogUtil.e(requestId, "MMS network acquiring failure", e);
260                     result = SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS;
261                     break;
262                 } catch (MmsHttpException e) {
263                     LogUtil.e(requestId, "HTTP or network I/O failure", e);
264                     result = SmsManager.MMS_ERROR_HTTP_FAILURE;
265                     httpStatusCode = e.getStatusCode();
266                     // Retry
267                 } catch (Exception e) {
268                     LogUtil.e(requestId, "Unexpected failure", e);
269                     result = SmsManager.MMS_ERROR_UNSPECIFIED;
270                     break;
271                 } finally {
272                     // Release the MMS network immediately except successful DownloadRequest.
273                     networkManager.releaseNetwork(requestId,
274                             this instanceof DownloadRequest
275                                     && result == Activity.RESULT_OK);
276                     stopListeningToDataConnectionState(connectionStateCallback);
277                 }
278 
279                 if (result != Activity.RESULT_CANCELED) {
280                     try { // Cool down retry if the previous attempt wasn't voluntarily cancelled.
281                         new CountDownLatch(1).await(retryDelaySecs, TimeUnit.SECONDS);
282                     } catch (InterruptedException e) { }
283                     // Double the cool down time if the next try fails again.
284                     retryDelaySecs <<= 1;
285                 }
286             }
287         }
288         processResult(context, result, response, httpStatusCode, /* handledByCarrierApp= */ false,
289                 retryId);
290     }
291 
listenToDataConnectionState(MonitorTelephonyCallback connectionStateCallback)292     private void listenToDataConnectionState(MonitorTelephonyCallback connectionStateCallback) {
293         final TelephonyManager telephonyManager = mContext.getSystemService(
294                 TelephonyManager.class).createForSubscriptionId(mSubId);
295         telephonyManager.registerTelephonyCallback(r -> r.run(), connectionStateCallback);
296     }
297 
stopListeningToDataConnectionState( MonitorTelephonyCallback connectionStateCallback)298     private void stopListeningToDataConnectionState(
299             MonitorTelephonyCallback connectionStateCallback) {
300         final TelephonyManager telephonyManager = mContext.getSystemService(
301                 TelephonyManager.class).createForSubscriptionId(mSubId);
302         telephonyManager.unregisterTelephonyCallback(connectionStateCallback);
303     }
304 
305     /**
306      * Process the result of the completed request, including updating the message status
307      * in database and sending back the result via pending intents.
308      * @param context The context
309      * @param result The result code of execution
310      * @param response The response body
311      * @param httpStatusCode The optional http status code in case of http failure
312      * @param handledByCarrierApp True if the sending/downloading was handled by a carrier app
313      *                            rather than MmsService.
314      */
processResult(Context context, int result, byte[] response, int httpStatusCode, boolean handledByCarrierApp)315     public void processResult(Context context, int result, byte[] response, int httpStatusCode,
316             boolean handledByCarrierApp) {
317         processResult(context, result, response, httpStatusCode, handledByCarrierApp, 0);
318     }
319 
processResult(Context context, int result, byte[] response, int httpStatusCode, boolean handledByCarrierApp, int retryId)320     private void processResult(Context context, int result, byte[] response, int httpStatusCode,
321             boolean handledByCarrierApp, int retryId) {
322         final Uri messageUri = persistIfRequired(context, result, response);
323 
324         final String requestId = this.getRequestId();
325         currentState = result == Activity.RESULT_OK ? MmsRequestState.Success
326                 : MmsRequestState.Failure;
327         // As noted in the @param comment above, the httpStatusCode is only set when there's
328         // an http failure. On success, such as an http code of 200, the value here will be 0.
329         // "httpStatusCode: xxx" is now reported for an http failure only.
330         LogUtil.i(requestId, "processResult: "
331                 + (result == Activity.RESULT_OK ? "success" : "failure(" + result + ")")
332                 + (httpStatusCode != 0 ? ", httpStatusCode: " + httpStatusCode : "")
333                 + " handledByCarrierApp: " + handledByCarrierApp
334                 + " mLastConnectionFailure: " + mLastConnectionFailure);
335 
336         // Return MMS HTTP request result via PendingIntent
337         final PendingIntent pendingIntent = getPendingIntent();
338         if (pendingIntent != null) {
339             boolean succeeded = true;
340             // Extra information to send back with the pending intent
341             Intent fillIn = new Intent();
342             if (response != null) {
343                 succeeded = transferResponse(fillIn, response);
344             }
345             if (messageUri != null) {
346                 fillIn.putExtra("uri", messageUri.toString());
347             }
348             if (result == SmsManager.MMS_ERROR_HTTP_FAILURE && httpStatusCode != 0) {
349                 fillIn.putExtra(SmsManager.EXTRA_MMS_HTTP_STATUS, httpStatusCode);
350             }
351             fillIn.putExtra(EXTRA_LAST_CONNECTION_FAILURE_CAUSE_CODE,
352                     mLastConnectionFailure);
353             fillIn.putExtra(EXTRA_HANDLED_BY_CARRIER_APP, handledByCarrierApp);
354             try {
355                 if (!succeeded) {
356                     result = SmsManager.MMS_ERROR_IO_ERROR;
357                 }
358                 reportPossibleAnomaly(result, httpStatusCode);
359                 pendingIntent.send(context, result, fillIn);
360                 mMmsStats.addAtomToStorage(result, retryId, handledByCarrierApp, mMessageId);
361             } catch (PendingIntent.CanceledException e) {
362                 LogUtil.e(requestId, "Sending pending intent canceled", e);
363             }
364         }
365 
366         revokeUriPermission(context);
367     }
368 
reportPossibleAnomaly(int result, int httpStatusCode)369     private void reportPossibleAnomaly(int result, int httpStatusCode) {
370         switch (result) {
371             case SmsManager.MMS_ERROR_HTTP_FAILURE:
372                 if (isPoorSignal()) {
373                     LogUtil.i(this.toString(), "Poor Signal");
374                     break;
375                 }
376             case SmsManager.MMS_ERROR_INVALID_APN:
377             case SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS:
378             case SmsManager.MMS_ERROR_UNSPECIFIED:
379             case SmsManager.MMS_ERROR_IO_ERROR:
380                 String message = "MMS failed";
381                 LogUtil.i(this.toString(),
382                         message + " with error: " + result + " httpStatus:" + httpStatusCode);
383                 reportAnomaly(message, generateUUID(result, httpStatusCode));
384                 break;
385             default:
386                 break;
387         }
388     }
389 
reportAnomaly(@onNull String anomalyMsg, @NonNull UUID uuid)390     private void reportAnomaly(@NonNull String anomalyMsg, @NonNull UUID uuid) {
391         TelephonyManager telephonyManager =
392                 mContext.getSystemService(TelephonyManager.class)
393                         .createForSubscriptionId(mSubId);
394         if (telephonyManager != null) {
395             AnomalyReporter.reportAnomaly(
396                     uuid,
397                     anomalyMsg,
398                     telephonyManager.getSimCarrierId());
399         }
400     }
401 
generateUUID(int result, int httpStatusCode)402     private UUID generateUUID(int result, int httpStatusCode) {
403         long lresult = result;
404         long lhttpStatusCode = httpStatusCode;
405         return new UUID(MmsConstants.MMS_ANOMALY_UUID.getMostSignificantBits(),
406                 MmsConstants.MMS_ANOMALY_UUID.getLeastSignificantBits()
407                         + ((lhttpStatusCode << 32) + lresult));
408     }
409 
isPoorSignal()410     private boolean isPoorSignal() {
411         // Check Wifi signal strength when IMS registers via Wifi
412         if (isImsOnWifi()) {
413             int rssi = 0;
414             WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
415             final WifiInfo wifiInfo = wifiManager.getConnectionInfo();
416             if (wifiInfo != null) {
417                 rssi = wifiInfo.getRssi();
418             } else {
419                 return false;
420             }
421             final int wifiLevel = wifiManager.calculateSignalLevel(rssi);
422             LogUtil.d(this.toString(), "Wifi signal rssi: " + rssi + " level:" + wifiLevel);
423             if (wifiLevel <= SIGNAL_LEVEL_THRESHOLD) {
424                 return true;
425             }
426             return false;
427         } else {
428             // Check cellular signal strength
429             final TelephonyManager telephonyManager = mContext.getSystemService(
430                     TelephonyManager.class).createForSubscriptionId(mSubId);
431             final int cellLevel = telephonyManager.getSignalStrength().getLevel();
432             LogUtil.d(this.toString(), "Cellular signal level:" + cellLevel);
433             if (cellLevel <= SIGNAL_LEVEL_THRESHOLD) {
434                 return true;
435             }
436             return false;
437         }
438     }
439 
isImsOnWifi()440     private boolean isImsOnWifi() {
441         ImsMmTelManager imsManager;
442         try {
443             imsManager = ImsMmTelManager.createForSubscriptionId(mSubId);
444         } catch (IllegalArgumentException e) {
445             LogUtil.e(this.toString(), "invalid subid:" + mSubId);
446             return false;
447         }
448         if (imsManager != null) {
449             return imsManager.isAvailable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
450                     ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
451         } else {
452             return false;
453         }
454     }
455 
456     /**
457      * Returns true if sending / downloading using the carrier app has failed and completes the
458      * action using platform API's, otherwise false.
459      */
maybeFallbackToRegularDelivery(int carrierMessagingAppResult)460     protected boolean maybeFallbackToRegularDelivery(int carrierMessagingAppResult) {
461         if (carrierMessagingAppResult
462                 == CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK
463                 || carrierMessagingAppResult
464                         == CarrierMessagingService.DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK) {
465             LogUtil.d(this.toString(), "Sending/downloading MMS by IP failed. "
466                     + MmsService.formatCrossStackMessageId(mMessageId));
467             mRequestManager.addSimRequest(MmsRequest.this);
468             return true;
469         } else {
470             return false;
471         }
472     }
473 
474     /**
475      * Converts from {@code carrierMessagingAppResult} to a platform result code.
476      */
toSmsManagerResult(int carrierMessagingAppResult)477     protected static int toSmsManagerResult(int carrierMessagingAppResult) {
478         switch (carrierMessagingAppResult) {
479             case CarrierMessagingService.SEND_STATUS_OK:
480                 return Activity.RESULT_OK;
481             case CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK:
482                 return SmsManager.MMS_ERROR_RETRY;
483             default:
484                 return SmsManager.MMS_ERROR_UNSPECIFIED;
485         }
486     }
487 
488     /**
489      * Converts from {@code carrierMessagingAppResult} to a platform result code for outbound MMS
490      * requests.
491      */
toSmsManagerResultForOutboundMms(int carrierMessagingAppResult)492     protected static int toSmsManagerResultForOutboundMms(int carrierMessagingAppResult) {
493         if (Flags.temporaryFailuresInCarrierMessagingService()) {
494             switch (carrierMessagingAppResult) {
495                 case CarrierMessagingService.SEND_STATUS_OK:
496                     // TODO: b/378931437 - Update to an SmsManager result code when one is
497                     // available.
498                     return Activity.RESULT_OK;
499                 case CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK, // fall through
500                     CarrierMessagingService.SEND_STATUS_MMS_ERROR_RETRY:
501                     return SmsManager.MMS_ERROR_RETRY;
502                 case CarrierMessagingService.SEND_STATUS_ERROR: // fall through
503                 case CarrierMessagingService.SEND_STATUS_MMS_ERROR_UNSPECIFIED:
504                     return SmsManager.MMS_ERROR_UNSPECIFIED;
505                 case CarrierMessagingService.SEND_STATUS_MMS_ERROR_INVALID_APN:
506                     return SmsManager.MMS_ERROR_INVALID_APN;
507                 case CarrierMessagingService.SEND_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS:
508                     return SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS;
509                 case CarrierMessagingService.SEND_STATUS_MMS_ERROR_HTTP_FAILURE:
510                     return SmsManager.MMS_ERROR_HTTP_FAILURE;
511                 case CarrierMessagingService.SEND_STATUS_MMS_ERROR_IO_ERROR:
512                     return SmsManager.MMS_ERROR_IO_ERROR;
513                 case CarrierMessagingService.SEND_STATUS_MMS_ERROR_CONFIGURATION_ERROR:
514                     return SmsManager.MMS_ERROR_CONFIGURATION_ERROR;
515                 case CarrierMessagingService.SEND_STATUS_MMS_ERROR_NO_DATA_NETWORK:
516                     return SmsManager.MMS_ERROR_NO_DATA_NETWORK;
517                 case CarrierMessagingService.SEND_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID:
518                     return SmsManager.MMS_ERROR_INVALID_SUBSCRIPTION_ID;
519                 case CarrierMessagingService.SEND_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION:
520                     return SmsManager.MMS_ERROR_INACTIVE_SUBSCRIPTION;
521                 case CarrierMessagingService.SEND_STATUS_MMS_ERROR_DATA_DISABLED:
522                     return SmsManager.MMS_ERROR_DATA_DISABLED;
523                 case CarrierMessagingService.SEND_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER:
524                     return SmsManager.MMS_ERROR_MMS_DISABLED_BY_CARRIER;
525                 default:
526                     return SmsManager.MMS_ERROR_UNSPECIFIED;
527             }
528         } else {
529             return toSmsManagerResult(carrierMessagingAppResult);
530         }
531     }
532 
533     /**
534      * Converts from {@code carrierMessagingAppResult} to a platform result code for download MMS
535      * requests.
536      */
toSmsManagerResultForInboundMms(int carrierMessagingAppResult)537     protected static int toSmsManagerResultForInboundMms(int carrierMessagingAppResult) {
538         if (Flags.temporaryFailuresInCarrierMessagingService()) {
539             switch (carrierMessagingAppResult) {
540                 case CarrierMessagingService.DOWNLOAD_STATUS_OK:
541                     return Activity.RESULT_OK;
542                 case CarrierMessagingService.DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK:
543                     return SmsManager.MMS_ERROR_RETRY;
544                 case CarrierMessagingService.DOWNLOAD_STATUS_ERROR: // fall through
545                 case CarrierMessagingService.DOWNLOAD_STATUS_MMS_ERROR_UNSPECIFIED:
546                     return SmsManager.MMS_ERROR_UNSPECIFIED;
547                 case CarrierMessagingService.DOWNLOAD_STATUS_MMS_ERROR_INVALID_APN:
548                     return SmsManager.MMS_ERROR_INVALID_APN;
549                 case CarrierMessagingService.DOWNLOAD_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS:
550                     return SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS;
551                 case CarrierMessagingService.DOWNLOAD_STATUS_MMS_ERROR_HTTP_FAILURE:
552                     return SmsManager.MMS_ERROR_HTTP_FAILURE;
553                 case CarrierMessagingService.DOWNLOAD_STATUS_MMS_ERROR_IO_ERROR:
554                     return SmsManager.MMS_ERROR_IO_ERROR;
555                 case CarrierMessagingService.DOWNLOAD_STATUS_MMS_ERROR_RETRY:
556                     return SmsManager.MMS_ERROR_RETRY;
557                 case CarrierMessagingService.DOWNLOAD_STATUS_MMS_ERROR_CONFIGURATION_ERROR:
558                     return SmsManager.MMS_ERROR_CONFIGURATION_ERROR;
559                 case CarrierMessagingService.DOWNLOAD_STATUS_MMS_ERROR_NO_DATA_NETWORK:
560                     return SmsManager.MMS_ERROR_NO_DATA_NETWORK;
561                 case CarrierMessagingService.DOWNLOAD_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID:
562                     return SmsManager.MMS_ERROR_INVALID_SUBSCRIPTION_ID;
563                 case CarrierMessagingService.DOWNLOAD_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION:
564                     return SmsManager.MMS_ERROR_INACTIVE_SUBSCRIPTION;
565                 case CarrierMessagingService.DOWNLOAD_STATUS_MMS_ERROR_DATA_DISABLED:
566                     return SmsManager.MMS_ERROR_DATA_DISABLED;
567                 case CarrierMessagingService.DOWNLOAD_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER:
568                     return SmsManager.MMS_ERROR_MMS_DISABLED_BY_CARRIER;
569                 default:
570                     return SmsManager.MMS_ERROR_UNSPECIFIED;
571             }
572         } else {
573             return toSmsManagerResult(carrierMessagingAppResult);
574         }
575     }
576 
577     @Override
toString()578     public String toString() {
579         return getClass().getSimpleName() + '@' + Integer.toHexString(hashCode())
580                 + " " + MmsService.formatCrossStackMessageId(mMessageId)
581                 + " subId: " + mSubId
582                 + " currentState: \"" + currentState.name() + "\""
583                 + " result: " + result;
584     }
585 
getRequestId()586     protected String getRequestId() {
587         return this.toString();
588     }
589 
590     /**
591      * Making the HTTP request to MMSC
592      *
593      * @param context The context
594      * @param netMgr The current {@link MmsNetworkManager}
595      * @param apn The APN setting
596      * @return The HTTP response data
597      * @throws MmsHttpException If any network error happens
598      */
doHttp(Context context, MmsNetworkManager netMgr, ApnSettings apn)599     protected abstract byte[] doHttp(Context context, MmsNetworkManager netMgr, ApnSettings apn)
600             throws MmsHttpException;
601 
602     /**
603      * @return The PendingIntent associate with the MMS sending invocation
604      */
getPendingIntent()605     protected abstract PendingIntent getPendingIntent();
606 
607     /**
608      * @return The queue should be used by this request, 0 is sending and 1 is downloading
609      */
getQueueType()610     protected abstract int getQueueType();
611 
612     /**
613      * Persist message into telephony if required (i.e. when auto-persisting is on or
614      * the calling app is non-default sms app for sending)
615      *
616      * @param context The context
617      * @param result The result code of execution
618      * @param response The response body
619      * @return The persisted URI of the message or null if we don't persist or fail
620      */
persistIfRequired(Context context, int result, byte[] response)621     protected abstract Uri persistIfRequired(Context context, int result, byte[] response);
622 
623     /**
624      * Prepare to make the HTTP request - will download message for sending
625      * @return true if preparation succeeds (and request can proceed) else false
626      */
prepareForHttpRequest()627     protected abstract boolean prepareForHttpRequest();
628 
629     /**
630      * Transfer the received response to the caller
631      *
632      * @param fillIn the intent that will be returned to the caller
633      * @param response the pdu to transfer
634      * @return true if response transfer succeeds else false
635      */
transferResponse(Intent fillIn, byte[] response)636     protected abstract boolean transferResponse(Intent fillIn, byte[] response);
637 
638     /**
639      * Revoke the content URI permission granted by the MMS app to the phone package.
640      *
641      * @param context The context
642      */
revokeUriPermission(Context context)643     protected abstract void revokeUriPermission(Context context);
644 
645     /**
646      * Base class for handling carrier app send / download result.
647      */
648     protected abstract class CarrierMmsActionCallback implements CarrierMessagingCallback {
649         @Override
onSendSmsComplete(int result, int messageRef)650         public void onSendSmsComplete(int result, int messageRef) {
651             LogUtil.e("Unexpected onSendSmsComplete call for "
652                     + MmsService.formatCrossStackMessageId(mMessageId)
653                     + " with result: " + result);
654         }
655 
656         @Override
onSendMultipartSmsComplete(int result, int[] messageRefs)657         public void onSendMultipartSmsComplete(int result, int[] messageRefs) {
658             LogUtil.e("Unexpected onSendMultipartSmsComplete call for "
659                     + MmsService.formatCrossStackMessageId(mMessageId)
660                     + " with result: " + result);
661         }
662 
663         @Override
onReceiveSmsComplete(int result)664         public void onReceiveSmsComplete(int result) {
665             LogUtil.e("Unexpected onFilterComplete call for "
666                     + MmsService.formatCrossStackMessageId(mMessageId)
667                     + " with result: " + result);
668         }
669     }
670 
671     /**
672      * Get the size of the pdu to send or download.
673      */
getPayloadSize()674     protected abstract long getPayloadSize();
675 
676     /**
677      * Determine whether the send or to-be-downloaded pdu is within size limits for the
678      * current connection.
679      */
680     @VisibleForTesting
canTransferPayloadOnCurrentNetwork()681     public boolean canTransferPayloadOnCurrentNetwork() {
682         ServiceState serviceState = mTelephonyManager.getServiceState();
683         if (serviceState == null) {
684             // serviceState can be null when the subscription is inactive
685             // or when there was an error communicating with the phone process.
686             LogUtil.d("canTransferPayloadOnCurrentNetwork serviceState null");
687             return true;    // assume we're not connected to a satellite
688         }
689         long payloadSize = getPayloadSize();
690         int maxPduSize = mMmsConfig
691                 .getInt(CarrierConfigManager.KEY_MMS_MAX_NTN_PAYLOAD_SIZE_BYTES_INT);
692         LogUtil.d("canTransferPayloadOnCurrentNetwork payloadSize: " + payloadSize
693                 + " maxPduSize: " + maxPduSize);
694         return payloadSize > 0 && (maxPduSize == -1 || payloadSize <= maxPduSize);
695     }
696 }
697