blob: cefafc766b080a946e640f49ff7466b5653f9b35 [file] [log] [blame]
/*
* 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 com.android.mms.service.exception.MmsNetworkException;
import com.android.mms.service.http.NameResolver;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* Manages the MMS network connectivity
*/
public class MmsNetworkManager implements NameResolver {
// 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);
private 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 NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
.build();
// The callback to register when we request MMS network
private ConnectivityManager.NetworkCallback mNetworkCallback;
private ConnectivityManager mConnectivityManager;
// TODO: we need to re-architect this when we support MSIM, like maybe one manager for each SIM?
public MmsNetworkManager(Context context) {
mContext = context;
mNetworkCallback = null;
mNetwork = null;
mMmsRequestCount = 0;
mConnectivityManager = null;
}
public Network getNetwork() {
synchronized (this) {
return mNetwork;
}
}
/**
* 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");
releaseRequest(mNetworkCallback);
resetLocked();
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) {
releaseRequest(mNetworkCallback);
resetLocked();
}
}
}
}
/**
* 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) {
releaseRequest(this);
if (mNetworkCallback == this) {
resetLocked();
}
MmsNetworkManager.this.notifyAll();
}
}
@Override
public void onUnavailable() {
super.onUnavailable();
Log.d(MmsService.TAG, "NetworkCallbackListener.onUnavailable");
synchronized (MmsNetworkManager.this) {
releaseRequest(this);
if (mNetworkCallback == this) {
resetLocked();
}
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 releaseRequest(ConnectivityManager.NetworkCallback callback) {
if (callback != null) {
final ConnectivityManager connectivityManager = getConnectivityManager();
connectivityManager.unregisterNetworkCallback(callback);
}
}
/**
* Reset the state
*/
private void resetLocked() {
mNetworkCallback = null;
mNetwork = null;
mMmsRequestCount = 0;
}
@Override
public InetAddress[] getAllByName(String host) throws UnknownHostException {
synchronized (this) {
if (mNetwork != null) {
return mNetwork.getAllByName(host);
}
return new InetAddress[0];
}
}
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;
}
}