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          * @return read pdu (else null in case of error or too big)
88          */
readPduFromContentUri(final Uri contentUri, final int maxSize)89         public byte[] readPduFromContentUri(final Uri contentUri, final int maxSize);
90 
91         /**
92          * Write pdu to supplied content uri
93          * @param contentUri content uri to which bytes should be written
94          * @param pdu pdu bytes to write
95          * @return true in case of success (else false)
96          */
writePduToContentUri(final Uri contentUri, final byte[] pdu)97         public boolean writePduToContentUri(final Uri contentUri, final byte[] pdu);
98     }
99 
100     // The reference to the pending requests manager (i.e. the MmsService)
101     protected RequestManager mRequestManager;
102     // The SIM id
103     protected int mSubId;
104     // The creator app
105     protected String mCreator;
106     // MMS config
107     protected Bundle mMmsConfig;
108     // Context used to get TelephonyManager.
109     protected Context mContext;
110     protected long mMessageId;
111     protected int mLastConnectionFailure;
112     private MmsStats mMmsStats;
113     private int result;
114     private int httpStatusCode;
115     protected TelephonyManager mTelephonyManager;
116     @VisibleForTesting
117     public int SATELLITE_MMS_SIZE_LIMIT = 3 * 1024;    // TODO - read from a carrier config setting
118 
119     protected enum MmsRequestState {
120         Unknown,
121         Created,
122         PrepareForHttpRequest,
123         AcquiringNetwork,
124         LoadingApn,
125         DoingHttp,
126         Success,
127         Failure
128     };
129     protected MmsRequestState currentState = MmsRequestState.Unknown;
130 
131     class MonitorTelephonyCallback extends TelephonyCallback implements
132             TelephonyCallback.PreciseDataConnectionStateListener {
133 
134         /** The lock to update mNetworkIdToApn. */
135         private final Object mLock = new Object();
136         /**
137          * Track the network agent Id to APN. Usually we have at most 2 networks that are capable of
138          * MMS at the same time (terrestrial and satellite)
139          */
140         @GuardedBy("mLock")
141         private final SparseArray<ApnSetting> mNetworkIdToApn = new SparseArray<>(2);
142         @Override
onPreciseDataConnectionStateChanged( @onNull PreciseDataConnectionState connectionState)143         public void onPreciseDataConnectionStateChanged(
144                 @NonNull PreciseDataConnectionState connectionState) {
145             ApnSetting apnSetting = connectionState.getApnSetting();
146             if (apnSetting != null) {
147                 // Only track networks that are capable of MMS.
148                 if ((apnSetting.getApnTypeBitmask() & ApnSetting.TYPE_MMS) != 0) {
149                     LogUtil.d("onPreciseDataConnectionStateChanged: " + connectionState);
150                     mLastConnectionFailure = connectionState.getLastCauseCode();
151                     if (Flags.mmsGetApnFromPdsc()) {
152                         synchronized (mLock) {
153                             mNetworkIdToApn.put(connectionState.getNetId(), apnSetting);
154                         }
155                     }
156                 }
157             }
158         }
159     }
160 
MmsRequest(RequestManager requestManager, int subId, String creator, Bundle mmsConfig, Context context, long messageId, MmsStats mmsStats, TelephonyManager telephonyManager)161     public MmsRequest(RequestManager requestManager, int subId, String creator,
162             Bundle mmsConfig, Context context, long messageId, MmsStats mmsStats,
163             TelephonyManager telephonyManager) {
164         currentState = MmsRequestState.Created;
165         mRequestManager = requestManager;
166         mSubId = subId;
167         mCreator = creator;
168         mMmsConfig = mmsConfig;
169         mContext = context;
170         mMessageId = messageId;
171         mMmsStats = mmsStats;
172         mTelephonyManager = telephonyManager;
173     }
174 
getSubId()175     public int getSubId() {
176         return mSubId;
177     }
178 
179     /**
180      * Execute the request
181      *
182      * @param context The context
183      * @param networkManager The network manager to use
184      */
execute(Context context, MmsNetworkManager networkManager)185     public void execute(Context context, MmsNetworkManager networkManager) {
186         final String requestId = this.getRequestId();
187         LogUtil.i(requestId, "Executing...");
188         result = SmsManager.MMS_ERROR_UNSPECIFIED;
189         httpStatusCode = 0;
190         byte[] response = null;
191         int retryId = 0;
192         currentState = MmsRequestState.PrepareForHttpRequest;
193 
194         if (!prepareForHttpRequest()) { // Prepare request, like reading pdu data from user
195             LogUtil.e(requestId, "Failed to prepare for request");
196             result = SmsManager.MMS_ERROR_IO_ERROR;
197         } else { // Execute
198             long retryDelaySecs = 2;
199             // Try multiple times of MMS HTTP request, depending on the error.
200             for (retryId = 0; retryId < RETRY_TIMES; retryId++) {
201                 httpStatusCode = 0; // Clear for retry.
202                 MonitorTelephonyCallback connectionStateCallback = new MonitorTelephonyCallback();
203                 try {
204                     listenToDataConnectionState(connectionStateCallback);
205                     currentState = MmsRequestState.AcquiringNetwork;
206                     int networkId = networkManager.acquireNetwork(requestId);
207                     currentState = MmsRequestState.LoadingApn;
208                     ApnSettings apn = null;
209                     ApnSetting networkApn = null;
210                     if (Flags.mmsGetApnFromPdsc()) {
211                         synchronized (connectionStateCallback.mLock) {
212                             networkApn = connectionStateCallback.mNetworkIdToApn.get(networkId);
213                         }
214                         if (networkApn != null) {
215                             apn = ApnSettings.getApnSettingsFromNetworkApn(networkApn);
216                         }
217                     }
218                     if (apn == null) {
219                         final String apnName = networkManager.getApnName();
220                         LogUtil.d(requestId, "APN name is " + apnName);
221                         try {
222                             apn = ApnSettings.load(context, apnName, mSubId, requestId);
223                         } catch (ApnException e) {
224                             // If no APN could be found, fall back to trying without the APN name
225                             if (apnName == null) {
226                                 // If the APN name was already null then don't need to retry
227                                 throw (e);
228                             }
229                             LogUtil.i(requestId, "No match with APN name: "
230                                     + apnName + ", try with no name");
231                             apn = ApnSettings.load(context, null, mSubId, requestId);
232                         }
233                     }
234 
235                     if (Flags.mmsGetApnFromPdsc() && networkApn == null && apn != null) {
236                         reportAnomaly("Can't find MMS APN in mms network",
237                                 UUID.fromString("2bdda74d-3cf4-44ad-a87f-24c961212a6f"));
238                     }
239 
240                     LogUtil.d(requestId, "Using APN " + apn);
241                     if (Flags.carrierEnabledSatelliteFlag()
242                             && networkManager.isSatelliteTransport()
243                             && !canTransferPayloadOnCurrentNetwork()) {
244                         LogUtil.e(requestId, "PDU too large for satellite");
245                         result = SmsManager.MMS_ERROR_TOO_LARGE_FOR_TRANSPORT;
246                         break;
247                     }
248                     currentState = MmsRequestState.DoingHttp;
249                     response = doHttp(context, networkManager, apn);
250                     result = Activity.RESULT_OK;
251                     // Success
252                     break;
253                 } catch (ApnException e) {
254                     LogUtil.e(requestId, "APN failure", e);
255                     result = SmsManager.MMS_ERROR_INVALID_APN;
256                     break;
257                 } catch (MmsNetworkException e) {
258                     LogUtil.e(requestId, "MMS network acquiring failure", e);
259                     result = SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS;
260                     break;
261                 } catch (MmsHttpException e) {
262                     LogUtil.e(requestId, "HTTP or network I/O failure", e);
263                     result = SmsManager.MMS_ERROR_HTTP_FAILURE;
264                     httpStatusCode = e.getStatusCode();
265                     // Retry
266                 } catch (Exception e) {
267                     LogUtil.e(requestId, "Unexpected failure", e);
268                     result = SmsManager.MMS_ERROR_UNSPECIFIED;
269                     break;
270                 } finally {
271                     // Release the MMS network immediately except successful DownloadRequest.
272                     networkManager.releaseNetwork(requestId,
273                             this instanceof DownloadRequest
274                                     && result == Activity.RESULT_OK);
275                     stopListeningToDataConnectionState(connectionStateCallback);
276                 }
277 
278                 if (result != Activity.RESULT_CANCELED) {
279                     try { // Cool down retry if the previous attempt wasn't voluntarily cancelled.
280                         new CountDownLatch(1).await(retryDelaySecs, TimeUnit.SECONDS);
281                     } catch (InterruptedException e) { }
282                     // Double the cool down time if the next try fails again.
283                     retryDelaySecs <<= 1;
284                 }
285             }
286         }
287         processResult(context, result, response, httpStatusCode, /* handledByCarrierApp= */ false,
288                 retryId);
289     }
290 
listenToDataConnectionState(MonitorTelephonyCallback connectionStateCallback)291     private void listenToDataConnectionState(MonitorTelephonyCallback connectionStateCallback) {
292         final TelephonyManager telephonyManager = mContext.getSystemService(
293                 TelephonyManager.class).createForSubscriptionId(mSubId);
294         telephonyManager.registerTelephonyCallback(r -> r.run(), connectionStateCallback);
295     }
296 
stopListeningToDataConnectionState( MonitorTelephonyCallback connectionStateCallback)297     private void stopListeningToDataConnectionState(
298             MonitorTelephonyCallback connectionStateCallback) {
299         final TelephonyManager telephonyManager = mContext.getSystemService(
300                 TelephonyManager.class).createForSubscriptionId(mSubId);
301         telephonyManager.unregisterTelephonyCallback(connectionStateCallback);
302     }
303 
304     /**
305      * Process the result of the completed request, including updating the message status
306      * in database and sending back the result via pending intents.
307      * @param context The context
308      * @param result The result code of execution
309      * @param response The response body
310      * @param httpStatusCode The optional http status code in case of http failure
311      * @param handledByCarrierApp True if the sending/downloading was handled by a carrier app
312      *                            rather than MmsService.
313      */
processResult(Context context, int result, byte[] response, int httpStatusCode, boolean handledByCarrierApp)314     public void processResult(Context context, int result, byte[] response, int httpStatusCode,
315             boolean handledByCarrierApp) {
316         processResult(context, result, response, httpStatusCode, handledByCarrierApp, 0);
317     }
318 
processResult(Context context, int result, byte[] response, int httpStatusCode, boolean handledByCarrierApp, int retryId)319     private void processResult(Context context, int result, byte[] response, int httpStatusCode,
320             boolean handledByCarrierApp, int retryId) {
321         final Uri messageUri = persistIfRequired(context, result, response);
322 
323         final String requestId = this.getRequestId();
324         currentState = result == Activity.RESULT_OK ? MmsRequestState.Success
325                 : MmsRequestState.Failure;
326         // As noted in the @param comment above, the httpStatusCode is only set when there's
327         // an http failure. On success, such as an http code of 200, the value here will be 0.
328         // "httpStatusCode: xxx" is now reported for an http failure only.
329         LogUtil.i(requestId, "processResult: "
330                 + (result == Activity.RESULT_OK ? "success" : "failure(" + result + ")")
331                 + (httpStatusCode != 0 ? ", httpStatusCode: " + httpStatusCode : "")
332                 + " handledByCarrierApp: " + handledByCarrierApp
333                 + " mLastConnectionFailure: " + mLastConnectionFailure);
334 
335         // Return MMS HTTP request result via PendingIntent
336         final PendingIntent pendingIntent = getPendingIntent();
337         if (pendingIntent != null) {
338             boolean succeeded = true;
339             // Extra information to send back with the pending intent
340             Intent fillIn = new Intent();
341             if (response != null) {
342                 succeeded = transferResponse(fillIn, response);
343             }
344             if (messageUri != null) {
345                 fillIn.putExtra("uri", messageUri.toString());
346             }
347             if (result == SmsManager.MMS_ERROR_HTTP_FAILURE && httpStatusCode != 0) {
348                 fillIn.putExtra(SmsManager.EXTRA_MMS_HTTP_STATUS, httpStatusCode);
349             }
350             fillIn.putExtra(EXTRA_LAST_CONNECTION_FAILURE_CAUSE_CODE,
351                     mLastConnectionFailure);
352             fillIn.putExtra(EXTRA_HANDLED_BY_CARRIER_APP, handledByCarrierApp);
353             try {
354                 if (!succeeded) {
355                     result = SmsManager.MMS_ERROR_IO_ERROR;
356                 }
357                 reportPossibleAnomaly(result, httpStatusCode);
358                 pendingIntent.send(context, result, fillIn);
359                 mMmsStats.addAtomToStorage(result, retryId, handledByCarrierApp, mMessageId);
360             } catch (PendingIntent.CanceledException e) {
361                 LogUtil.e(requestId, "Sending pending intent canceled", e);
362             }
363         }
364 
365         revokeUriPermission(context);
366     }
367 
reportPossibleAnomaly(int result, int httpStatusCode)368     private void reportPossibleAnomaly(int result, int httpStatusCode) {
369         switch (result) {
370             case SmsManager.MMS_ERROR_HTTP_FAILURE:
371                 if (isPoorSignal()) {
372                     LogUtil.i(this.toString(), "Poor Signal");
373                     break;
374                 }
375             case SmsManager.MMS_ERROR_INVALID_APN:
376             case SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS:
377             case SmsManager.MMS_ERROR_UNSPECIFIED:
378             case SmsManager.MMS_ERROR_IO_ERROR:
379                 String message = "MMS failed";
380                 LogUtil.i(this.toString(),
381                         message + " with error: " + result + " httpStatus:" + httpStatusCode);
382                 reportAnomaly(message, generateUUID(result, httpStatusCode));
383                 break;
384             default:
385                 break;
386         }
387     }
388 
reportAnomaly(@onNull String anomalyMsg, @NonNull UUID uuid)389     private void reportAnomaly(@NonNull String anomalyMsg, @NonNull UUID uuid) {
390         TelephonyManager telephonyManager =
391                 mContext.getSystemService(TelephonyManager.class)
392                         .createForSubscriptionId(mSubId);
393         if (telephonyManager != null) {
394             AnomalyReporter.reportAnomaly(
395                     uuid,
396                     anomalyMsg,
397                     telephonyManager.getSimCarrierId());
398         }
399     }
400 
generateUUID(int result, int httpStatusCode)401     private UUID generateUUID(int result, int httpStatusCode) {
402         long lresult = result;
403         long lhttpStatusCode = httpStatusCode;
404         return new UUID(MmsConstants.MMS_ANOMALY_UUID.getMostSignificantBits(),
405                 MmsConstants.MMS_ANOMALY_UUID.getLeastSignificantBits()
406                         + ((lhttpStatusCode << 32) + lresult));
407     }
408 
isPoorSignal()409     private boolean isPoorSignal() {
410         // Check Wifi signal strength when IMS registers via Wifi
411         if (isImsOnWifi()) {
412             int rssi = 0;
413             WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
414             final WifiInfo wifiInfo = wifiManager.getConnectionInfo();
415             if (wifiInfo != null) {
416                 rssi = wifiInfo.getRssi();
417             } else {
418                 return false;
419             }
420             final int wifiLevel = wifiManager.calculateSignalLevel(rssi);
421             LogUtil.d(this.toString(), "Wifi signal rssi: " + rssi + " level:" + wifiLevel);
422             if (wifiLevel <= SIGNAL_LEVEL_THRESHOLD) {
423                 return true;
424             }
425             return false;
426         } else {
427             // Check cellular signal strength
428             final TelephonyManager telephonyManager = mContext.getSystemService(
429                     TelephonyManager.class).createForSubscriptionId(mSubId);
430             final int cellLevel = telephonyManager.getSignalStrength().getLevel();
431             LogUtil.d(this.toString(), "Cellular signal level:" + cellLevel);
432             if (cellLevel <= SIGNAL_LEVEL_THRESHOLD) {
433                 return true;
434             }
435             return false;
436         }
437     }
438 
isImsOnWifi()439     private boolean isImsOnWifi() {
440         ImsMmTelManager imsManager;
441         try {
442             imsManager = ImsMmTelManager.createForSubscriptionId(mSubId);
443         } catch (IllegalArgumentException e) {
444             LogUtil.e(this.toString(), "invalid subid:" + mSubId);
445             return false;
446         }
447         if (imsManager != null) {
448             return imsManager.isAvailable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
449                     ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
450         } else {
451             return false;
452         }
453     }
454 
455     /**
456      * Returns true if sending / downloading using the carrier app has failed and completes the
457      * action using platform API's, otherwise false.
458      */
maybeFallbackToRegularDelivery(int carrierMessagingAppResult)459     protected boolean maybeFallbackToRegularDelivery(int carrierMessagingAppResult) {
460         if (carrierMessagingAppResult
461                 == CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK
462                 || carrierMessagingAppResult
463                         == CarrierMessagingService.DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK) {
464             LogUtil.d(this.toString(), "Sending/downloading MMS by IP failed. "
465                     + MmsService.formatCrossStackMessageId(mMessageId));
466             mRequestManager.addSimRequest(MmsRequest.this);
467             return true;
468         } else {
469             return false;
470         }
471     }
472 
473     /**
474      * Converts from {@code carrierMessagingAppResult} to a platform result code.
475      */
toSmsManagerResult(int carrierMessagingAppResult)476     protected static int toSmsManagerResult(int carrierMessagingAppResult) {
477         switch (carrierMessagingAppResult) {
478             case CarrierMessagingService.SEND_STATUS_OK:
479                 return Activity.RESULT_OK;
480             case CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK:
481                 return SmsManager.MMS_ERROR_RETRY;
482             default:
483                 return SmsManager.MMS_ERROR_UNSPECIFIED;
484         }
485     }
486 
487     @Override
toString()488     public String toString() {
489         return getClass().getSimpleName() + '@' + Integer.toHexString(hashCode())
490                 + " " + MmsService.formatCrossStackMessageId(mMessageId)
491                 + " subId: " + mSubId
492                 + " currentState: \"" + currentState.name() + "\""
493                 + " result: " + result;
494     }
495 
getRequestId()496     protected String getRequestId() {
497         return this.toString();
498     }
499 
500     /**
501      * Making the HTTP request to MMSC
502      *
503      * @param context The context
504      * @param netMgr The current {@link MmsNetworkManager}
505      * @param apn The APN setting
506      * @return The HTTP response data
507      * @throws MmsHttpException If any network error happens
508      */
doHttp(Context context, MmsNetworkManager netMgr, ApnSettings apn)509     protected abstract byte[] doHttp(Context context, MmsNetworkManager netMgr, ApnSettings apn)
510             throws MmsHttpException;
511 
512     /**
513      * @return The PendingIntent associate with the MMS sending invocation
514      */
getPendingIntent()515     protected abstract PendingIntent getPendingIntent();
516 
517     /**
518      * @return The queue should be used by this request, 0 is sending and 1 is downloading
519      */
getQueueType()520     protected abstract int getQueueType();
521 
522     /**
523      * Persist message into telephony if required (i.e. when auto-persisting is on or
524      * the calling app is non-default sms app for sending)
525      *
526      * @param context The context
527      * @param result The result code of execution
528      * @param response The response body
529      * @return The persisted URI of the message or null if we don't persist or fail
530      */
persistIfRequired(Context context, int result, byte[] response)531     protected abstract Uri persistIfRequired(Context context, int result, byte[] response);
532 
533     /**
534      * Prepare to make the HTTP request - will download message for sending
535      * @return true if preparation succeeds (and request can proceed) else false
536      */
prepareForHttpRequest()537     protected abstract boolean prepareForHttpRequest();
538 
539     /**
540      * Transfer the received response to the caller
541      *
542      * @param fillIn the intent that will be returned to the caller
543      * @param response the pdu to transfer
544      * @return true if response transfer succeeds else false
545      */
transferResponse(Intent fillIn, byte[] response)546     protected abstract boolean transferResponse(Intent fillIn, byte[] response);
547 
548     /**
549      * Revoke the content URI permission granted by the MMS app to the phone package.
550      *
551      * @param context The context
552      */
revokeUriPermission(Context context)553     protected abstract void revokeUriPermission(Context context);
554 
555     /**
556      * Base class for handling carrier app send / download result.
557      */
558     protected abstract class CarrierMmsActionCallback implements CarrierMessagingCallback {
559         @Override
onSendSmsComplete(int result, int messageRef)560         public void onSendSmsComplete(int result, int messageRef) {
561             LogUtil.e("Unexpected onSendSmsComplete call for "
562                     + MmsService.formatCrossStackMessageId(mMessageId)
563                     + " with result: " + result);
564         }
565 
566         @Override
onSendMultipartSmsComplete(int result, int[] messageRefs)567         public void onSendMultipartSmsComplete(int result, int[] messageRefs) {
568             LogUtil.e("Unexpected onSendMultipartSmsComplete call for "
569                     + MmsService.formatCrossStackMessageId(mMessageId)
570                     + " with result: " + result);
571         }
572 
573         @Override
onReceiveSmsComplete(int result)574         public void onReceiveSmsComplete(int result) {
575             LogUtil.e("Unexpected onFilterComplete call for "
576                     + MmsService.formatCrossStackMessageId(mMessageId)
577                     + " with result: " + result);
578         }
579     }
580 
581     /**
582      * Get the size of the pdu to send or download.
583      */
getPayloadSize()584     protected abstract long getPayloadSize();
585 
586     /**
587      * Determine whether the send or to-be-downloaded pdu is within size limits for the
588      * current connection.
589      */
590     @VisibleForTesting
canTransferPayloadOnCurrentNetwork()591     public boolean canTransferPayloadOnCurrentNetwork() {
592         ServiceState serviceState = mTelephonyManager.getServiceState();
593         if (serviceState == null) {
594             // serviceState can be null when the subscription is inactive
595             // or when there was an error communicating with the phone process.
596             LogUtil.d("canTransferPayloadOnCurrentNetwork serviceState null");
597             return true;    // assume we're not connected to a satellite
598         }
599         long payloadSize = getPayloadSize();
600         int maxPduSize = mMmsConfig
601                 .getInt(CarrierConfigManager.KEY_MMS_MAX_NTN_PAYLOAD_SIZE_BYTES_INT);
602         LogUtil.d("canTransferPayloadOnCurrentNetwork payloadSize: " + payloadSize
603                 + " maxPduSize: " + maxPduSize);
604         return payloadSize > 0 && payloadSize <= maxPduSize;
605     }
606 }
607