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