数据业务中的短连接,是一种为了满足业务需求,临时建立起来的连接。当业务完成通信需求后,这种数据连接会被框架释放掉。与之相对,长连接一旦拨号成功就会一直存在下去,除非用户主动关闭或者终端受到网络等因素的影响导致连接不可用。
一种比较常见的例子,就是发送彩信时,终端将建立短连接;当彩信发送结束时,短连接将被释放掉。
在这篇博客中,我们就从彩信入手,看看Android中是如何建立和释放短连接的。
1 MmsService
Android中彩信相关的应用为MmsService,我们看看它AndroidManifest.xml中的部分片段:
<application android:label="MmsService" android:process="com.android.phone" android:usesCleartextTraffic="true">
<service android:name=".MmsService" android:enabled="true" android:exported="true"/>
</application>
容易看出MmsService是运行在Phone进程中的。在这篇博客中,我们不深入研究彩信服务的启动和收发彩信的过程,主要看看彩信如何建立和释放短连接。
在MmsService.java中,每一次发送彩信均会形成一个MmsRequest(抽象类,实现类为SendRequest和DownloadRequest),并将其加入到运行队列中:
private void addToRunningRequestQueueSynchronized(final MmsRequest request) {
...........
final int queue = request.getQueueType();
//判读queue值的有效性
............
mRunningRequestCount++;
mCurrentSubId = request.getSubId();
mRunningRequestExecutors[queue].execute(new Runnable() {
@Override
public void run() {
try {
//MmsRequest执行
request.execute(MmsService.this, getNetworkManager(request.getSubId()));
} finally {
synchronized (MmsService.this) {
mRunningRequestCount--;
if (mRunningRequestCount <= 0) {
//将位于pending队列中的请求,加入到running队列中
movePendingSimRequestsToRunningSynchronized();
}
}
}
}
}
}
在上面对代码中,利用getNetworkManager创建了MmsRequest专属的MmsNetworkManger:
//subId对应于发送彩信的卡
private MmsNetworkManager getNetworkManager(int subId) {
synchronized (mNetworkManagerCache) {
MmsNetworkManager manager = mNetworkManagerCache.get(subId);
if (manager == null) {
manager = new MmsNetworkManager(this, subId);
mNetworkManagerCache.put(subId, manager);
}
return manager;
}
}
先看看MmsNetworkManager的构造函数:
public MmsNetworkManager(Context context, int subId) {
............
//构造出申请网络的request
//注意Capability指定为MMS,同时NetworkSpecifier指定为对应的sub id
mNetworkRequest = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
.setNetworkSpecifier(Integer.toString(mSubId))
.build();
}
接下来,看看MmsRequest的execute方法:
public void execute(Context context, MmsNetworkManager networkManager) {
..........
//检查是否做好准备工作
if (!ensureMmsConfigLoaded()) {
........
} else if (!prepareForHttpRequest()){
........
} else {
long retryDelaySecs = 2;
for (int i = 0; i < RETRY_TIMES; i++) {
try {
//利用MmsNetworkManager建立短连接
networkManager.acquireNetwork(requestId);
try {
//进行实际的发送或接收
............
} finally {
//利用MmsNetworkManager释放段连接
networkManager.releaseNetwork(requestId);
}
} .........//进行捕获异常等操作
}
//处理发送结果
processResult(context, result, response, httpStatusCode);
}
}
2 建立短连接
MmsNetworkManager中的acquireNetwork负责申请网络:
public void acquireNetwork(final String requestId) throws MmsNetworkException {
synchronized (this) {
mMmsRequestCount += 1;
//若之前已经申请过网络,则不重新申请
if (mNetwork != null) {
return;
}
if (mNetworkCallback == null) {
//申请网络
startNewNetworkRequestLocked();
}
//定义申请网络所应耗费的时间
final long shouldEnd = SystemClock.elapsedRealtime() + NETWORK_ACQUIRE_TIMEOUT_MILLIS;
long waitTime = NETWORK_ACQUIRE_TIMEOUT_MILLIS;
while (waitTime > 0) {
try {
//等待;申请网络后,ConnectivityService将调用MmsNetworkManager的回调函数,在回调函数进行通知
this.wait(waitTime);
} catch (InterruptedException e) {
............
}
if (mNetwork != null) {
//成功获取到网络
return;
}
waitTime = shouldEnd - SystemClock.elapsedRealtime();
}
//获取网络失败,释放请求
releaseRequestLocked(mNetworkCallback);
throw new MmsNetworkException("Acquiring network timed out");
}
}
顺着流程,看看MmsNetworkManager中的startNewNetworkRequestLocked:
private void startNewNetworkRequestLocked() {
final ConnectivityManager connectivityManager = getConnectivityManager();
//定义回调函数
mNetworkCallback = new NetworkRequestCallback();
//利用ConnectivityManager向ConnectivityService发送请求,并注册回调函数
connectivityManager.requestNetwork(
mNetworkRequest, mNetworkCallback, NETWORK_REQUEST_TIMEOUT_MILLIS);
}
在进入connectivityManager前,先看看MmsNetworkManager中的回调函数:
private class NetworkRequestCallback extends ConnectivityManager.NetworkCallback {
@Override
public void onAvailable(Network network) {
super.onAvailable(network);
synchronized (MmsNetworkManager.this) {
//网络可用时,存储并通知
//于是acquireNetwork函数,可以返回结果
mNetwork = network;
MmsNetworkManager.this.notifyAll();
}
}
//网络断开和不可用时的回调函数
............
}
现在我们可以看看connectivityManager的requestNetwork函数:
public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
int timeoutMs) {
requestNetwork(request, networkCallback, timeoutMs,
//根据网络能力返回type;在之前的版本中会返回TYPE_MOBILE_MMS,android7.0中返回TYPE_NONE
inferLegacyTypeForNetworkCapabilities(request.networkCapabilities));
}
public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
int timeoutMs, int legacyType) {
sendRequestForNetwork(request.networkCapabilities, networkCallback, timeoutMs, REQUEST,
legacyType);
}
private NetworkRequest sendRequestForNetwork(NetworkCapabilities need,
NetworkCallback networkCallback, int timeoutSec, int action,
int legacyType) {
...................
try {
..............
synchronized(sNetworkCallback) {
if (action == LISTEN) {
//这种类型的Request仅用来监听网络的变化,无法申请实际的网络
.............
} else {
//调用ConnectivityService的requestNetwork函数
networkCallback.networkRequest = mService.requestNetwork(need,
new Messenger(sCallbackHandler), timeoutSec, new Binder(), legacyType);
}
}
} catch(RemoteException e) {
............
}
..............
}
从上面的代码,可以看到申请网络还是需要依靠ConnectivityService:
@Override
public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
//检查参数有效性
............
//利用参数构造networkRequest及NetworkRequestInfo
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
nextNetworkRequestId());
NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder, type);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri));
....................
}
ConnectivityService将调用handleRegisterNetworkRequestWithIntent处理EVENT_REGISTER_NETWORK_REQUEST:
private void handleRegisterNetworkRequestWithIntent(Message msg) {
//检查msg携带Request的有效性
............
handleRegisterNetworkRequest(nri);
}
private void handleRegisterNetworkRequest(NetworkRequestInfo nri) {
mNetworkRequests.put(nri.request, nri);
if (!nri.isRequest()) {
//非申请网络的request
................
}
//重新匹配所有的NetworkAgent和NetworkRequest
rematchAllNetworksAndRequests(null, 0);
//默认建立的长连接对应的NetworkAgent,其capabilities不包含MMS,可以看看DataConnection中的makeNetworkCapabilities函数
//因此无论数据业务开关是否打开,此时mNetworkForRequestId.get(nri.request.requestId)返回值均为null
if (nri.isRequest() && mNetworkForRequestId.get(nri.request.requestId) == null) {
//发送request给所有的NetworkFactory,分数为0
sendUpdatedScoreToFactories(nri.request, 0);
}
}
与之前介绍数据拨号准备工作的博客中描述的一样,请求MMS网络的NetworkRequest将同时发送给TelephonyNetworkFactory和PhoneSwitcher中创建的NetworkFactory。
这里需要注意的是,TelephonyNetworkFactory申明自己的网络能力时,指定匹配对应卡的subId;
PhoneSwitcher中创建的NetworkFactory申明自己的网络能力时,指定匹配所有的subId。
于是,不匹配MMS NetworkRequest对应subId的TelephonyNetworkFactory,将无法处理该NetworkRequest。
此外,当TelephonyNetworkFactory处于激活态时,才能处理NetworkRequest。
综上所述,我们可以得出结论:
当具有数据能力的Phone发送彩信时,对应的TelephonyNetworkFactory可以直接处理;
当不具有数据能力的Phone发送彩信时,没有TelephonyNetworkFactory可以处理MMS Request;此时,必须先通过PhoneSwitcher切换Phone的数据能力,然后再进行处理,这个过程也被成为DDS。
我们来看看DDS的过程。在PhoneSwitcher中,负责处理NetworkRequest的函数为onRequestNetwork:
private void onRequestNetwork(NetworkRequest networkRequest) {
//之前分析过,将NetworkRequest封装成DcRequest时,按照xml中的配置,定义了优先级
final DcRequest dcRequest = new DcRequest(networkRequest, mContext);
if (mPrioritizedDcRequests.contains(dcRequest) == false) {
mPrioritizedDcRequests.add(dcRequest);
//MMS request的优先级高于default,于是会排在最前面
Collections.sort(mPrioritizedDcRequests);
onEvaluate(REQUESTS_CHANGED, "netRequest");
}
}
private void onEvaluate(boolean requestsChanged, String reason) {
..............
if (diffDetected) {
List<Integer> newActivePhones = new ArrayList<Integer>();
int phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest);
if (phoneIdForRequest == INVALID_PHONE_INDEX) continue;
if (newActivePhones.contains(phoneIdForRequest)) continue;
//MMS NetworkRequest的phone id加入到newActivePhones
newActivePhones.add(phoneIdForRequest);
if (newActivePhones.size() >= mMaxActivePhones) break;
}
.............
for (int phoneId = 0; phoneId < mNumPhones; phoneId++) {
if (newActivePhones.contains(phoneId) == false) {
//注销不匹配MMS NetworkRequest subId的phone的数据能力
//通过RIL下发命令,完成后通知观察者
deactivate(phoneId);
}
}
for (int phoneId : newActivePhones) {
//赋予MMS Request subId对应phone的数据能力
//通过RIL下发命令,完成后通知观察者,TelephonyNetworkFactory
activate(phoneId);
}
}
}
不论TelephonyNetworkFactory可以直接处理MMS NetworkRequest,还是DDS后才能进行处理,最终TelephonyNetworkFactory将调用DcTracker的requestNetwork函数。
之后,框架就会激活彩信对应的APN,并用彩信APN进行拨号。这部分流程与之前博客描述的,数据拨号的准备工作及数据长连接拨号基本一致,不再赘述。
3 释放短连接
MmsNetworkManager中的releaseRequestLocked负责释放短连接:
private void releaseRequestLocked(ConnectivityManager.NetworkCallback callback) {
if (callback != null) {
final ConnectivityManager connectivityManager = getConnectivityManager();
try {
connectivityManager.unregisterNetworkCallback(callback);
} catch (IllegalArgumentException e) {
............
}
}
............
}
进入到ConnectivityManager的unregisterNetworkCallback函数:
public void unregisterNetworkCallback(NetworkCallback networkCallback) {
.............
try {
mService.releaseNetworkRequest(networkCallback.networkRequest);
} catch(RemoteException e) {
............
}
............
}
随着流程进入到ConnectivityService:
@Override
public void releaseNetworkRequest(NetworkRequest networkRequest) {
mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST, getCallingUid(),
0, networkRequest));
}
private void handleReleaseNetworkRequestWithIntent(PendingIntent pendingIntent,
int callingUid) {
NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent);
if (nri != null) {
handleReleaseNetworkRequest(nri.request, callingUid);
}
}
private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) {
NetworkRequestInfo nri = mNetworkRequests.get(request);
if (nri != null) {
.............
if (nri.isRequest()) {
...............
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
//释放每个NetworkAgent中对networkRequest的记录
if (nai.networkRequests.get(nri.request.requestId) != null) {
nai.networkRequests.remove(nri.request.requestId);
...........
//该NetworkAgent不再是任何NetworkRequest的最优匹配对象
if (unneeded(nai)) {
//注销掉该NetworkAgent,调用AsyncChannel的disconnect函数,与之前博客所述的长连接去拨号流程一致
//MMS建立的NetworkAgent将在此处被注销
teardownUnneededNetwork(nai);
} else {
..............
}
}
}
//移除ConnectivityService中NetworkAgentInfo的记录信息
NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
if (nai != null) {
mNetworkForRequestId.remove(nri.request.requestId);
}
............
for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
//移除NetworkFactory中关于该NetworkRequest的记录
//该消息将通知给PhoneSwitcher中创建的NetworkFactory和TelephonyNetworkFactory
nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST,
nri.request);
}
} else {
.........
}
.......
}
}
当PhoneSwitcher中创建的NetworkFactory收到CMD_CANCEL_REQUEST消息后,最终将调用onReleaseNetwork进行处理:
private void onReleaseNetwork(NetworkRequest networkRequest) {
final DcRequest dcRequest = new DcRequest(networkRequest, mContext);
//移除MMS对应的DcRequest
if (mPrioritizedDcRequests.remove(dcRequest)) {
//重新评估所有的DcRequest
//此时,由于高优先级的MMS DcRequest被移除了,于是Default data request成为了最优先的request
//如果之前为了发送彩信进行过DDS,那么此时将重新进行DDS,将数据能力切换到原来的default subId对应的phone
onEvaluate(REQUESTS_CHANGED, "netReleased");
}
}
当TelephonyNetworkFactory收到CMD_CANCEL_REQUEST消息后,同样会调用自己的onReleaseNetworkFor函数进行处理:
private void onReleaseNetworkFor(Message msg) {
//移除对NetworkRequest的记录
.............
if (mIsActive && isApplicable) {
String s = "onReleaseNetworkFor";
localLog.log(s);
log(s + " " + networkRequest);
//调用DcTracker的releaseNetwork函数,将去激活MMS对应APN,同时移除MMS对应的dataConnection
mDcTracker.releaseNetwork(networkRequest, localLog);
} else {
String s = "not releasing - isApp=" + isApplicable + ", isAct=" + mIsActive;
localLog.log(s);
log(s + " " + networkRequest);
}
}
注意到TelephonyNetworkFactory调用DcTracker的releaseNetwork函数有个前提条件,那就是该TelephonyNetworkFactory处于active状态。
考虑到ConnectivityService也发送过CMD_CANCEL_REQUEST给PhoneSwitcher,
因此可能由于时序上的原因,导致TelephonyNetworkFactory在执行onReleaseNetworkFor之前,PhoneSwitcher先进行了DDS。
此时TelephonyNetworkFactory将无法通过onReleaseNetworkFor来完成releaseNetwork的操作。
为了避免这个问题,TelephonyNetworkFactory监听了DDS变化的动作:
private void onActivePhoneSwitch() {
final boolean newIsActive = mPhoneSwitcher.isPhoneActive(mPhoneId);
if (mIsActive != newIsActive) {
mIsActive = newIsActive;
if (mIsDefault) {
applyRequests(mDefaultRequests, (mIsActive ? REQUEST : RELEASE), logString);
}
applyRequests(mSpecificRequests, (mIsActive ? REQUEST : RELEASE), logString);
}
}
private void applyRequests(HashMap<NetworkRequest, LocalLog> requestMap, boolean action,
String logStr) {
for (NetworkRequest networkRequest : requestMap.keySet()) {
if (action == REQUEST) {
mDcTracker.requestNetwork(networkRequest, localLog);
} else {
//同样调用了releaseNetwork
mDcTracker.releaseNetwork(networkRequest, localLog);
}
}
}
综上所述我们可以看到:
若PhoneSwitcher先进行了DDS,那么TelephonyNetworkFactory将通过applyRequests来调用releaseNetwork;然后在onReleaseNetworkFor中仅进行清除NetworkRequest的记录;
若TelephonyNetworkFactory先调用onReleaseNetworkFor,那么将清除NetworkRequest的记录并执行releaseNetwork;发生DDS后,由于已经清除过NetworkRequest的记录,于是不会重复执行releaseNetwork。
结束语
我们从MMS入手,分析了数据业务中短连接的建立和断开过程。
可以看到短连接主要利用ConnectivityManager的接口,来完成建立和断开的操作,同时在必要的时候利用PhoneSwitcher完成DDS。
之后短连接将依赖于长连接的建立和断开流程,完成实际的操作。