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.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.net.ConnectivityManager; 25 import android.net.Network; 26 import android.net.NetworkCapabilities; 27 import android.net.NetworkInfo; 28 import android.net.NetworkRequest; 29 import android.net.TelephonyNetworkSpecifier; 30 import android.os.Handler; 31 import android.os.Looper; 32 import android.os.Message; 33 import android.os.PersistableBundle; 34 import android.provider.DeviceConfig; 35 import android.telephony.CarrierConfigManager; 36 import android.telephony.SubscriptionManager; 37 import android.telephony.TelephonyManager; 38 39 import com.android.internal.annotations.VisibleForTesting; 40 import com.android.internal.telephony.PhoneConstants; 41 import com.android.mms.service.exception.MmsNetworkException; 42 43 /** 44 * Manages the MMS network connectivity 45 */ 46 public class MmsNetworkManager { 47 private static final String MMS_SERVICE_NETWORK_REQUEST_TIMEOUT_MILLIS = 48 "mms_service_network_request_timeout_millis"; 49 50 // Default timeout used to call ConnectivityManager.requestNetwork if the 51 // MMS_SERVICE_NETWORK_REQUEST_TIMEOUT_MILLIS flag is not set. 52 // Given that the telephony layer will retry on failures, this timeout should be high enough. 53 private static final int DEFAULT_MMS_SERVICE_NETWORK_REQUEST_TIMEOUT_MILLIS = 30 * 60 * 1000; 54 55 // Wait timeout for this class, this is an additional delay after waiting the network request 56 // timeout to make sure we don't bail prematurely. 57 private static final int ADDITIONAL_NETWORK_ACQUIRE_TIMEOUT_MILLIS = (5 * 1000); 58 59 /* Event created when receiving ACTION_CARRIER_CONFIG_CHANGED */ 60 private static final int EVENT_CARRIER_CONFIG_CHANGED = 1; 61 62 private final Context mContext; 63 64 // The requested MMS {@link android.net.Network} we are holding 65 // We need this when we unbind from it. This is also used to indicate if the 66 // MMS network is available. 67 private Network mNetwork; 68 // The current count of MMS requests that require the MMS network 69 // If mMmsRequestCount is 0, we should release the MMS network. 70 private int mMmsRequestCount; 71 // This is really just for using the capability 72 private final NetworkRequest mNetworkRequest; 73 // The callback to register when we request MMS network 74 private ConnectivityManager.NetworkCallback mNetworkCallback; 75 76 private volatile ConnectivityManager mConnectivityManager; 77 78 // The MMS HTTP client for this network 79 private MmsHttpClient mMmsHttpClient; 80 81 // The handler used for delayed release of the network 82 private final Handler mReleaseHandler; 83 84 // The task that does the delayed releasing of the network. 85 private final Runnable mNetworkReleaseTask; 86 87 // The SIM ID which we use to connect 88 private final int mSubId; 89 90 // The current Phone ID for this MmsNetworkManager 91 private int mPhoneId; 92 93 // If ACTION_SIM_CARD_STATE_CHANGED intent receiver is registered 94 private boolean mSimCardStateChangedReceiverRegistered; 95 96 private final Dependencies mDeps; 97 98 private int mNetworkReleaseTimeoutMillis; 99 100 // satellite transport status of associated mms active network 101 private boolean mIsSatelliteTransport; 102 103 private EventHandler mEventHandler; 104 105 private final class EventHandler extends Handler { EventHandler()106 EventHandler() { 107 super(Looper.getMainLooper()); 108 } 109 110 /** 111 * Handles events coming from the phone stack. Overridden from handler. 112 * 113 * @param msg the message to handle 114 */ 115 @Override handleMessage(Message msg)116 public void handleMessage(Message msg) { 117 switch (msg.what) { 118 case EVENT_CARRIER_CONFIG_CHANGED: 119 // Reload mNetworkReleaseTimeoutMillis from CarrierConfigManager. 120 handleCarrierConfigChanged(); 121 break; 122 default: 123 LogUtil.e("MmsNetworkManager: ignoring message of unexpected type " + msg.what); 124 } 125 } 126 } 127 128 /** 129 * This receiver listens to ACTION_SIM_CARD_STATE_CHANGED after starting a new NetworkRequest. 130 * If ACTION_SIM_CARD_STATE_CHANGED with SIM_STATE_ABSENT for a SIM card corresponding to the 131 * current NetworkRequest is received, it just releases the NetworkRequest without waiting for 132 * timeout. 133 */ 134 private final BroadcastReceiver mSimCardStateChangedReceiver = 135 new BroadcastReceiver() { 136 @Override 137 public void onReceive(Context context, Intent intent) { 138 final int simState = 139 intent.getIntExtra( 140 TelephonyManager.EXTRA_SIM_STATE, 141 TelephonyManager.SIM_STATE_UNKNOWN); 142 final int phoneId = 143 intent.getIntExtra( 144 PhoneConstants.PHONE_KEY, 145 SubscriptionManager.INVALID_PHONE_INDEX); 146 LogUtil.i("MmsNetworkManager: received ACTION_SIM_CARD_STATE_CHANGED" 147 + ", state=" + simStateString(simState) + ", phoneId=" + phoneId); 148 149 if (mPhoneId == phoneId && simState == TelephonyManager.SIM_STATE_ABSENT) { 150 synchronized (MmsNetworkManager.this) { 151 releaseRequestLocked(mNetworkCallback); 152 MmsNetworkManager.this.notifyAll(); 153 } 154 } 155 } 156 }; 157 simStateString(int state)158 private static String simStateString(int state) { 159 switch (state) { 160 case TelephonyManager.SIM_STATE_UNKNOWN: 161 return "UNKNOWN"; 162 case TelephonyManager.SIM_STATE_ABSENT: 163 return "ABSENT"; 164 case TelephonyManager.SIM_STATE_CARD_IO_ERROR: 165 return "CARD_IO_ERROR"; 166 case TelephonyManager.SIM_STATE_CARD_RESTRICTED: 167 return "CARD_RESTRICTED"; 168 case TelephonyManager.SIM_STATE_PRESENT: 169 return "PRESENT"; 170 default: 171 return "INVALID"; 172 } 173 } 174 175 /** 176 * This receiver listens to ACTION_CARRIER_CONFIG_CHANGED. Whenever receiving this event, 177 * mNetworkReleaseTimeoutMillis needs to be reloaded from CarrierConfigManager. 178 */ 179 private final BroadcastReceiver mCarrierConfigChangedReceiver = new BroadcastReceiver() { 180 @Override 181 public void onReceive(Context context, Intent intent) { 182 final String action = intent.getAction(); 183 if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action) 184 && mSubId == intent.getIntExtra( 185 CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, 186 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) { 187 mEventHandler.sendMessage(mEventHandler.obtainMessage( 188 EVENT_CARRIER_CONFIG_CHANGED)); 189 } 190 } 191 }; 192 handleCarrierConfigChanged()193 private void handleCarrierConfigChanged() { 194 final CarrierConfigManager configManager = 195 (CarrierConfigManager) 196 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); 197 final PersistableBundle config = configManager.getConfigForSubId(mSubId); 198 mNetworkReleaseTimeoutMillis = 199 config.getInt(CarrierConfigManager.KEY_MMS_NETWORK_RELEASE_TIMEOUT_MILLIS_INT); 200 LogUtil.d("MmsNetworkManager: handleCarrierConfigChanged() mNetworkReleaseTimeoutMillis " 201 + mNetworkReleaseTimeoutMillis); 202 } 203 204 /** 205 * Network callback for our network request 206 */ 207 private class NetworkRequestCallback extends ConnectivityManager.NetworkCallback { 208 @Override onLost(Network network)209 public void onLost(Network network) { 210 super.onLost(network); 211 LogUtil.w("NetworkCallbackListener.onLost: network=" + network); 212 synchronized (MmsNetworkManager.this) { 213 // Wait for other available network. Not notify. 214 if (network.equals(mNetwork)) { 215 mNetwork = null; 216 mMmsHttpClient = null; 217 } 218 } 219 } 220 221 @Override onUnavailable()222 public void onUnavailable() { 223 super.onUnavailable(); 224 LogUtil.w("NetworkCallbackListener.onUnavailable"); 225 synchronized (MmsNetworkManager.this) { 226 releaseRequestLocked(this); 227 MmsNetworkManager.this.notifyAll(); 228 } 229 } 230 231 @Override onCapabilitiesChanged(Network network, NetworkCapabilities nc)232 public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) { 233 // onAvailable will always immediately be followed by a onCapabilitiesChanged. Check 234 // network status here is enough. 235 super.onCapabilitiesChanged(network, nc); 236 LogUtil.w("NetworkCallbackListener.onCapabilitiesChanged: network=" 237 + network + ", nc=" + nc); 238 synchronized (MmsNetworkManager.this) { 239 final boolean isAvailable = 240 nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); 241 if (network.equals(mNetwork) && !isAvailable) { 242 // Current network becomes suspended. 243 mNetwork = null; 244 mMmsHttpClient = null; 245 // Not notify. Either wait for other available network or current network to 246 // become available again. 247 return; 248 } 249 250 // New available network 251 if (mNetwork == null && isAvailable) { 252 mIsSatelliteTransport = nc.hasTransport( 253 NetworkCapabilities.TRANSPORT_SATELLITE); 254 mNetwork = network; 255 MmsNetworkManager.this.notifyAll(); 256 } 257 } 258 } 259 } 260 261 /** 262 * Dependencies of MmsNetworkManager, for injection in tests. 263 */ 264 @VisibleForTesting 265 public static class Dependencies { 266 /** Get phone Id from the given subId */ getPhoneId(int subId)267 public int getPhoneId(int subId) { 268 return SubscriptionManager.getPhoneId(subId); 269 } 270 271 // Timeout used to call ConnectivityManager.requestNetwork. Given that the telephony layer 272 // will retry on failures, this timeout should be high enough. getNetworkRequestTimeoutMillis()273 public int getNetworkRequestTimeoutMillis() { 274 return DeviceConfig.getInt( 275 DeviceConfig.NAMESPACE_TELEPHONY, MMS_SERVICE_NETWORK_REQUEST_TIMEOUT_MILLIS, 276 DEFAULT_MMS_SERVICE_NETWORK_REQUEST_TIMEOUT_MILLIS); 277 } 278 getAdditionalNetworkAcquireTimeoutMillis()279 public int getAdditionalNetworkAcquireTimeoutMillis() { 280 return ADDITIONAL_NETWORK_ACQUIRE_TIMEOUT_MILLIS; 281 } 282 } 283 284 @VisibleForTesting MmsNetworkManager(Context context, int subId, Dependencies dependencies)285 protected MmsNetworkManager(Context context, int subId, Dependencies dependencies) { 286 mContext = context; 287 mDeps = dependencies; 288 mNetworkCallback = null; 289 mNetwork = null; 290 mMmsRequestCount = 0; 291 mConnectivityManager = null; 292 mMmsHttpClient = null; 293 mSubId = subId; 294 mReleaseHandler = new Handler(Looper.getMainLooper()); 295 296 NetworkRequest.Builder builder = new NetworkRequest.Builder() 297 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) 298 .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS) 299 .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder() 300 .setSubscriptionId(mSubId).build()); 301 302 // With Satellite internet support, add satellite transport with restricted capability to 303 // support mms over satellite network 304 builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); 305 try { 306 // TODO: b/331622062 remove the try/catch 307 builder.addTransportType(NetworkCapabilities.TRANSPORT_SATELLITE); 308 builder.removeCapability(NetworkCapabilities 309 .NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED); 310 } catch (IllegalArgumentException exception) { 311 LogUtil.e("TRANSPORT_SATELLITE or NOT_BANDWIDTH_CONSTRAINED is not supported."); 312 } 313 mNetworkRequest = builder.build(); 314 315 mNetworkReleaseTask = new Runnable() { 316 @Override 317 public void run() { 318 synchronized (this) { 319 if (mMmsRequestCount < 1) { 320 releaseRequestLocked(mNetworkCallback); 321 } 322 } 323 } 324 }; 325 326 mEventHandler = new EventHandler(); 327 // Register a receiver to listen to ACTION_CARRIER_CONFIG_CHANGED 328 mContext.registerReceiver( 329 mCarrierConfigChangedReceiver, 330 new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); 331 handleCarrierConfigChanged(); 332 } 333 MmsNetworkManager(Context context, int subId)334 public MmsNetworkManager(Context context, int subId) { 335 this(context, subId, new Dependencies()); 336 } 337 338 /** 339 * Acquire the MMS network 340 * 341 * @param requestId request ID for logging 342 * @throws com.android.mms.service.exception.MmsNetworkException if we fail to acquire it 343 * @return The net Id of the acquired network. 344 */ acquireNetwork(final String requestId)345 public int acquireNetwork(final String requestId) throws MmsNetworkException { 346 int networkRequestTimeoutMillis = mDeps.getNetworkRequestTimeoutMillis(); 347 348 synchronized (this) { 349 // Since we are acquiring the network, remove the network release task if exists. 350 mReleaseHandler.removeCallbacks(mNetworkReleaseTask); 351 mMmsRequestCount += 1; 352 if (mNetwork != null) { 353 // Already available 354 LogUtil.d(requestId, "MmsNetworkManager: already available"); 355 return mNetwork.getNetId(); 356 } 357 358 if (!mSimCardStateChangedReceiverRegistered) { 359 mPhoneId = mDeps.getPhoneId(mSubId); 360 if (mPhoneId == SubscriptionManager.INVALID_PHONE_INDEX 361 || mPhoneId == SubscriptionManager.DEFAULT_PHONE_INDEX) { 362 throw new MmsNetworkException("Invalid Phone Id: " + mPhoneId); 363 } 364 365 // Register a receiver to listen to ACTION_SIM_CARD_STATE_CHANGED 366 mContext.registerReceiver( 367 mSimCardStateChangedReceiver, 368 new IntentFilter(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED)); 369 mSimCardStateChangedReceiverRegistered = true; 370 } 371 372 // Not available, so start a new request if not done yet 373 if (mNetworkCallback == null) { 374 LogUtil.d(requestId, "MmsNetworkManager: start new network request"); 375 startNewNetworkRequestLocked(networkRequestTimeoutMillis); 376 } 377 378 try { 379 this.wait(networkRequestTimeoutMillis 380 + mDeps.getAdditionalNetworkAcquireTimeoutMillis()); 381 } catch (InterruptedException e) { 382 LogUtil.w(requestId, "MmsNetworkManager: acquire network wait interrupted"); 383 } 384 385 if (mSimCardStateChangedReceiverRegistered) { 386 // Unregister the receiver. 387 mContext.unregisterReceiver(mSimCardStateChangedReceiver); 388 mSimCardStateChangedReceiverRegistered = false; 389 } 390 391 if (mNetwork != null) { 392 // Success 393 return mNetwork.getNetId(); 394 } 395 396 if (mNetworkCallback != null) { // Timed out 397 LogUtil.e(requestId, 398 "MmsNetworkManager: timed out with networkRequestTimeoutMillis=" 399 + networkRequestTimeoutMillis 400 + " and ADDITIONAL_NETWORK_ACQUIRE_TIMEOUT_MILLIS=" 401 + mDeps.getAdditionalNetworkAcquireTimeoutMillis()); 402 // Release the network request and wake up all the MmsRequests for fast-fail 403 // together. 404 // TODO: Start new network request for remaining MmsRequests? 405 releaseRequestLocked(mNetworkCallback); 406 this.notifyAll(); 407 } 408 409 throw new MmsNetworkException("Acquiring network failed"); 410 } 411 } 412 413 /** 414 * Release the MMS network when nobody is holding on to it. 415 * 416 * @param requestId request ID for logging. 417 * @param shouldDelayRelease whether the release should be delayed for a carrier-configured 418 * timeout (default 5 seconds), the regular use case is to delay this 419 * for DownloadRequests to use the network for sending an 420 * acknowledgement on the same network. 421 */ releaseNetwork(final String requestId, final boolean shouldDelayRelease)422 public void releaseNetwork(final String requestId, final boolean shouldDelayRelease) { 423 synchronized (this) { 424 if (mMmsRequestCount > 0) { 425 mMmsRequestCount -= 1; 426 LogUtil.d(requestId, "MmsNetworkManager: release, count=" + mMmsRequestCount); 427 if (mMmsRequestCount < 1) { 428 if (shouldDelayRelease) { 429 // remove previously posted task and post a delayed task on the release 430 // handler to release the network 431 mReleaseHandler.removeCallbacks(mNetworkReleaseTask); 432 mReleaseHandler.postDelayed(mNetworkReleaseTask, 433 mNetworkReleaseTimeoutMillis); 434 } else { 435 releaseRequestLocked(mNetworkCallback); 436 } 437 } 438 } 439 } 440 } 441 442 /** 443 * Start a new {@link android.net.NetworkRequest} for MMS 444 */ startNewNetworkRequestLocked(int networkRequestTimeoutMillis)445 private void startNewNetworkRequestLocked(int networkRequestTimeoutMillis) { 446 final ConnectivityManager connectivityManager = getConnectivityManager(); 447 mNetworkCallback = new NetworkRequestCallback(); 448 connectivityManager.requestNetwork( 449 mNetworkRequest, mNetworkCallback, networkRequestTimeoutMillis); 450 } 451 452 /** 453 * Release the current {@link android.net.NetworkRequest} for MMS 454 * 455 * @param callback the {@link android.net.ConnectivityManager.NetworkCallback} to unregister 456 */ releaseRequestLocked(ConnectivityManager.NetworkCallback callback)457 private void releaseRequestLocked(ConnectivityManager.NetworkCallback callback) { 458 if (callback != null) { 459 final ConnectivityManager connectivityManager = getConnectivityManager(); 460 try { 461 connectivityManager.unregisterNetworkCallback(callback); 462 } catch (IllegalArgumentException e) { 463 // It is possible ConnectivityManager.requestNetwork may fail silently due 464 // to RemoteException. When that happens, we may get an invalid 465 // NetworkCallback, which causes an IllegalArgumentexception when we try to 466 // unregisterNetworkCallback. This exception in turn causes 467 // MmsNetworkManager to skip resetLocked() in the below. Thus MMS service 468 // would get stuck in the bad state until the device restarts. This fix 469 // catches the exception so that state clean up can be executed. 470 LogUtil.w("Unregister network callback exception", e); 471 } 472 } 473 resetLocked(); 474 } 475 476 /** 477 * Reset the state 478 */ resetLocked()479 private void resetLocked() { 480 mNetworkCallback = null; 481 mNetwork = null; 482 mMmsRequestCount = 0; 483 mMmsHttpClient = null; 484 } 485 getConnectivityManager()486 private @NonNull ConnectivityManager getConnectivityManager() { 487 if (mConnectivityManager == null) { 488 mConnectivityManager = (ConnectivityManager) mContext.getSystemService( 489 Context.CONNECTIVITY_SERVICE); 490 } 491 return mConnectivityManager; 492 } 493 494 /** 495 * Get an MmsHttpClient for the current network 496 * 497 * @return The MmsHttpClient instance 498 */ getOrCreateHttpClient()499 public MmsHttpClient getOrCreateHttpClient() { 500 synchronized (this) { 501 if (mMmsHttpClient == null) { 502 if (mNetwork != null) { 503 // Create new MmsHttpClient for the current Network 504 mMmsHttpClient = new MmsHttpClient(mContext, mNetwork, mConnectivityManager); 505 } 506 } 507 return mMmsHttpClient; 508 } 509 } 510 511 /** 512 * Get the APN name for the active network 513 * 514 * @return The APN name if available, otherwise null 515 */ getApnName()516 public String getApnName() { 517 Network network = null; 518 synchronized (this) { 519 if (mNetwork == null) { 520 return null; 521 } 522 network = mNetwork; 523 } 524 String apnName = null; 525 final ConnectivityManager connectivityManager = getConnectivityManager(); 526 final NetworkInfo mmsNetworkInfo = connectivityManager.getNetworkInfo(network); 527 if (mmsNetworkInfo != null) { 528 apnName = mmsNetworkInfo.getExtraInfo(); 529 } 530 return apnName; 531 } 532 533 @VisibleForTesting getNetworkReleaseTimeoutMillis()534 protected int getNetworkReleaseTimeoutMillis() { 535 return mNetworkReleaseTimeoutMillis; 536 } 537 538 /** 539 * Indicates satellite transport status for active network 540 * 541 * @return {@code true} if satellite transport, otherwise {@code false} 542 */ isSatelliteTransport()543 public boolean isSatelliteTransport() { 544 LogUtil.w("satellite transport status: " + mIsSatelliteTransport); 545 return mIsSatelliteTransport; 546 } 547 548 } 549