| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.mms.service; |
| |
| import android.content.Context; |
| import android.net.ConnectivityManager; |
| import android.net.Network; |
| import android.net.NetworkCapabilities; |
| import android.net.NetworkRequest; |
| import android.net.NetworkInfo; |
| import android.os.SystemClock; |
| import android.provider.Settings; |
| import android.util.Log; |
| |
| import com.android.mms.service.exception.MmsNetworkException; |
| import com.android.okhttp.ConnectionPool; |
| import com.android.okhttp.HostResolver; |
| |
| import java.net.InetAddress; |
| import java.net.UnknownHostException; |
| |
| /** |
| * Manages the MMS network connectivity |
| */ |
| public class MmsNetworkManager implements HostResolver { |
| // Timeout used to call ConnectivityManager.requestNetwork |
| private static final int NETWORK_REQUEST_TIMEOUT_MILLIS = 60 * 1000; |
| // Wait timeout for this class, a little bit longer than the above timeout |
| // to make sure we don't bail prematurely |
| private static final int NETWORK_ACQUIRE_TIMEOUT_MILLIS = |
| NETWORK_REQUEST_TIMEOUT_MILLIS + (5 * 1000); |
| |
| // Borrowed from {@link android.net.Network} |
| private static final boolean httpKeepAlive = |
| Boolean.parseBoolean(System.getProperty("http.keepAlive", "true")); |
| private static final int httpMaxConnections = |
| httpKeepAlive ? Integer.parseInt(System.getProperty("http.maxConnections", "5")) : 0; |
| private static final long httpKeepAliveDurationMs = |
| Long.parseLong(System.getProperty("http.keepAliveDuration", "300000")); // 5 minutes. |
| |
| private final Context mContext; |
| |
| // The requested MMS {@link android.net.Network} we are holding |
| // We need this when we unbind from it. This is also used to indicate if the |
| // MMS network is available. |
| private Network mNetwork; |
| // The current count of MMS requests that require the MMS network |
| // If mMmsRequestCount is 0, we should release the MMS network. |
| private int mMmsRequestCount; |
| // This is really just for using the capability |
| private final NetworkRequest mNetworkRequest; |
| // The callback to register when we request MMS network |
| private ConnectivityManager.NetworkCallback mNetworkCallback; |
| |
| private volatile ConnectivityManager mConnectivityManager; |
| |
| // The OkHttp's ConnectionPool used by the HTTP client associated with this network manager |
| private ConnectionPool mConnectionPool; |
| |
| // The MMS HTTP client for this network |
| private MmsHttpClient mMmsHttpClient; |
| |
| // The SIM ID which we use to connect |
| private final int mSubId; |
| |
| public MmsNetworkManager(Context context, int subId) { |
| mContext = context; |
| mNetworkCallback = null; |
| mNetwork = null; |
| mMmsRequestCount = 0; |
| mConnectivityManager = null; |
| mConnectionPool = null; |
| mMmsHttpClient = null; |
| mSubId = subId; |
| mNetworkRequest = new NetworkRequest.Builder() |
| .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) |
| .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS) |
| .setNetworkSpecifier(Integer.toString(mSubId)) |
| .build(); |
| } |
| |
| /** |
| * Acquire the MMS network |
| * |
| * @throws com.android.mms.service.exception.MmsNetworkException if we fail to acquire it |
| */ |
| public void acquireNetwork() throws MmsNetworkException { |
| if (inAirplaneMode()) { |
| // Fast fail airplane mode |
| throw new MmsNetworkException("In airplane mode"); |
| } |
| synchronized (this) { |
| mMmsRequestCount += 1; |
| if (mNetwork != null) { |
| // Already available |
| Log.d(MmsService.TAG, "MmsNetworkManager: already available"); |
| return; |
| } |
| Log.d(MmsService.TAG, "MmsNetworkManager: start new network request"); |
| // Not available, so start a new request |
| newRequest(); |
| final long shouldEnd = SystemClock.elapsedRealtime() + NETWORK_ACQUIRE_TIMEOUT_MILLIS; |
| long waitTime = NETWORK_ACQUIRE_TIMEOUT_MILLIS; |
| while (waitTime > 0) { |
| try { |
| this.wait(waitTime); |
| } catch (InterruptedException e) { |
| Log.w(MmsService.TAG, "MmsNetworkManager: acquire network wait interrupted"); |
| } |
| if (mNetwork != null) { |
| // Success |
| return; |
| } |
| // Calculate remaining waiting time to make sure we wait the full timeout period |
| waitTime = shouldEnd - SystemClock.elapsedRealtime(); |
| } |
| // Timed out, so release the request and fail |
| Log.d(MmsService.TAG, "MmsNetworkManager: timed out"); |
| releaseRequestLocked(mNetworkCallback); |
| throw new MmsNetworkException("Acquiring network timed out"); |
| } |
| } |
| |
| /** |
| * Release the MMS network when nobody is holding on to it. |
| */ |
| public void releaseNetwork() { |
| synchronized (this) { |
| if (mMmsRequestCount > 0) { |
| mMmsRequestCount -= 1; |
| Log.d(MmsService.TAG, "MmsNetworkManager: release, count=" + mMmsRequestCount); |
| if (mMmsRequestCount < 1) { |
| releaseRequestLocked(mNetworkCallback); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Start a new {@link android.net.NetworkRequest} for MMS |
| */ |
| private void newRequest() { |
| final ConnectivityManager connectivityManager = getConnectivityManager(); |
| mNetworkCallback = new ConnectivityManager.NetworkCallback() { |
| @Override |
| public void onAvailable(Network network) { |
| super.onAvailable(network); |
| Log.d(MmsService.TAG, "NetworkCallbackListener.onAvailable: network=" + network); |
| synchronized (MmsNetworkManager.this) { |
| mNetwork = network; |
| MmsNetworkManager.this.notifyAll(); |
| } |
| } |
| |
| @Override |
| public void onLost(Network network) { |
| super.onLost(network); |
| Log.d(MmsService.TAG, "NetworkCallbackListener.onLost: network=" + network); |
| synchronized (MmsNetworkManager.this) { |
| releaseRequestLocked(this); |
| MmsNetworkManager.this.notifyAll(); |
| } |
| } |
| |
| @Override |
| public void onUnavailable() { |
| super.onUnavailable(); |
| Log.d(MmsService.TAG, "NetworkCallbackListener.onUnavailable"); |
| synchronized (MmsNetworkManager.this) { |
| releaseRequestLocked(this); |
| MmsNetworkManager.this.notifyAll(); |
| } |
| } |
| }; |
| connectivityManager.requestNetwork( |
| mNetworkRequest, mNetworkCallback, NETWORK_REQUEST_TIMEOUT_MILLIS); |
| } |
| |
| /** |
| * Release the current {@link android.net.NetworkRequest} for MMS |
| * |
| * @param callback the {@link android.net.ConnectivityManager.NetworkCallback} to unregister |
| */ |
| private void releaseRequestLocked(ConnectivityManager.NetworkCallback callback) { |
| if (callback != null) { |
| final ConnectivityManager connectivityManager = getConnectivityManager(); |
| connectivityManager.unregisterNetworkCallback(callback); |
| } |
| resetLocked(); |
| } |
| |
| /** |
| * Reset the state |
| */ |
| private void resetLocked() { |
| mNetworkCallback = null; |
| mNetwork = null; |
| mMmsRequestCount = 0; |
| // Currently we follow what android.net.Network does with ConnectionPool, |
| // which is per Network object. So if Network changes, we should clear |
| // out the ConnectionPool and thus the MmsHttpClient (since it is linked |
| // to a specific ConnectionPool). |
| mConnectionPool = null; |
| mMmsHttpClient = null; |
| } |
| |
| private static final InetAddress[] EMPTY_ADDRESS_ARRAY = new InetAddress[0]; |
| @Override |
| public InetAddress[] getAllByName(String host) throws UnknownHostException { |
| Network network = null; |
| synchronized (this) { |
| if (mNetwork == null) { |
| return EMPTY_ADDRESS_ARRAY; |
| } |
| network = mNetwork; |
| } |
| return network.getAllByName(host); |
| } |
| |
| private ConnectivityManager getConnectivityManager() { |
| if (mConnectivityManager == null) { |
| mConnectivityManager = (ConnectivityManager) mContext.getSystemService( |
| Context.CONNECTIVITY_SERVICE); |
| } |
| return mConnectivityManager; |
| } |
| |
| private boolean inAirplaneMode() { |
| return Settings.System.getInt(mContext.getContentResolver(), |
| Settings.Global.AIRPLANE_MODE_ON, 0) != 0; |
| } |
| |
| private ConnectionPool getOrCreateConnectionPoolLocked() { |
| if (mConnectionPool == null) { |
| mConnectionPool = new ConnectionPool(httpMaxConnections, httpKeepAliveDurationMs); |
| } |
| return mConnectionPool; |
| } |
| |
| /** |
| * Get an MmsHttpClient for the current network |
| * |
| * @return The MmsHttpClient instance |
| */ |
| public MmsHttpClient getOrCreateHttpClient() { |
| synchronized (this) { |
| if (mMmsHttpClient == null) { |
| if (mNetwork != null) { |
| // Create new MmsHttpClient for the current Network |
| mMmsHttpClient = new MmsHttpClient( |
| mContext, |
| mNetwork.getSocketFactory(), |
| MmsNetworkManager.this, |
| getOrCreateConnectionPoolLocked()); |
| } |
| } |
| return mMmsHttpClient; |
| } |
| } |
| |
| /** |
| * Get the APN name for the active network |
| * |
| * @return The APN name if available, otherwise null |
| */ |
| public String getApnName() { |
| Network network = null; |
| synchronized (this) { |
| if (mNetwork == null) { |
| Log.d(MmsService.TAG, "MmsNetworkManager: getApnName: network not available"); |
| return null; |
| } |
| network = mNetwork; |
| } |
| String apnName = null; |
| final ConnectivityManager connectivityManager = getConnectivityManager(); |
| NetworkInfo mmsNetworkInfo = connectivityManager.getNetworkInfo(network); |
| if (mmsNetworkInfo != null) { |
| apnName = mmsNetworkInfo.getExtraInfo(); |
| } |
| Log.d(MmsService.TAG, "MmsNetworkManager: getApnName: " + apnName); |
| return apnName; |
| } |
| } |