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

来源:互联网 发布:网络弊大于利总结陈词 编辑:程序博客网 时间:2024/06/06 04:33

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

@Overridepublic 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:

@Overridepublic 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。
之后短连接将依赖于长连接的建立和断开流程,完成实际的操作。

1 0
原创粉丝点击