Android7.0 数据业务中的短连接

数据业务中的短连接,是一种为了满足业务需求,临时建立起来的连接。当业务完成通信需求后,这种数据连接会被框架释放掉。与之相对,长连接一旦拨号成功就会一直存在下去,除非用户主动关闭或者终端受到网络等因素的影响导致连接不可用。
一种比较常见的例子,就是发送彩信时,终端将建立短连接;当彩信发送结束时,短连接将被释放掉。
在这篇博客中,我们就从彩信入手,看看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。
之后短连接将依赖于长连接的建立和断开流程,完成实际的操作。

你可能感兴趣的:(android)