Android7.0 数据业务长连接拨号过程

前面我们已经分析了android在进行数据业务拨号前,进行相关准备工作的流程,现在我们可以分析一下整个数据业务长连接拨号在框架部分的流程。

长连接的“长”,是相对于终端进行彩信发送等操作时,建立的临时数据连接而言的(这种临时数据连接在业务执行完毕后,会主动断开),是能够长时间存在的数据连接。

1 CellDataPreference
我们从点击UI界面的数据拨号开关开始分析整个流程。
在原生的Android代码中,数据开关作为设置的一部分,相关的操作定义于CellDataPreference.java中,定义于packages/apps/settings/src/com/android/settings/datausage目录下。

我们看看处理点击操作的performClick函数:

@Override
protected void performClick(View view) {
	.............
	//开关处于开启状态
	if (mChecked) {
		//当前subId对应的卡信息(卡需要处于激活状态,即相关信息已经加载)
		final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo(mSubId);
		//默认数据卡对应的卡状态
		final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo();

		//showSimCardTile判断手机是否支持多卡,支持的话返回true
		//整个If的含义就是:仅支持单卡,或者默认数据卡与当前的卡信息一致
		if (!Utils.showSimCardTile(getContext()) || (nextSir != null && currentSir != null &&
		currentSir.getSubscriptionId() == nextSir.getSubscriptionId())) {
			//关闭数据业务(开关处于开启态,再点击一次,变成关闭态)
			setMobileDataEnabled(false);
			if (nextSir != null && currentSir != null &&
			currentSir.getSubscriptionId() == nextSir.getSubscriptionId()) {
				//双卡的情况下,还要关闭另一张卡的数据业务(当前卡为默认数据卡,这里是以防万一)
				disableDataForOtherSubscriptions(mSubId);
			}
			return;
		}
		............
		super.performClick(view);
	} else {
		//这里是从关到开的过程,多卡的情况
		if (Utils.showSimCardTile(getContext())) {
			//将标志位置为true
			mMultiSimDialog = true;
			//调用父类方法;在父类方法中最终将调用子类实现的onClick方法
			super.performClick(view);
		} else {
			//单卡时直接开始拨号
			setMobileDataEnabled(true);
		}
	}
}

从上面的代码我们可以看出,在多卡的情况下,将开关从关闭置为打开,将由CellDataPreference的onClick函数进行处理:

@Override
protected void onClick(DialogInterface dialog, int which) {
	if (which != DialogInterface.BUTTON_POSITIVE) {
		return;
	}

	//在前面的代码中,mMultiSimDialog已经置为true,表示手机支持多卡
	if (mMultiSimDialog) {
		//将当前CellDataPreference对应卡设为默认数据卡
		mSubscriptionManager.setDefaultDataSubId(mSubId);
		//开始数据拨号
		setMobileDataEnabled(true);
		//关闭另一张卡的数据业务
		disableDataForOtherSubscriptions(mSubId);
	} else {
		// TODO: extend to modify policy enabled flag.
		setMobileDataEnabled(false);
	}
}

private void setMobileDataEnabled(boolean enabled) {
	if (DataUsageSummary.LOGD) Log.d(TAG, "setMobileDataEnabled(" + enabled + "," + mSubId + ")");
	//调用TelephonyManager的接口
	mTelephonyManager.setDataEnabled(mSubId, enabled);
	//更改界面
	setChecked(enabled);
}

2 TelephonyManager
根据上文的代码,我们知道设置界面最终通过调用TelephonyManager开启拨号流程。

//传入参数subId为数据卡对应的subId
//enable为true表示开启数据业务;false表示关闭数据业务
@SystemApi
public void setDataEnabled(int subId, boolean enable) {
	try {
		Log.d(TAG, "setDataEnabled: enabled=" + enable);
		//获取binder代理对象
		ITelephony telephony = getITelephony();
		if (telephony != null)
			//通过binder通信调用接口
			telephony.setDataEnabled(subId, enable);
	} catch (RemoteException e) {
		Log.e(TAG, "Error calling ITelephony#setDataEnabled", e);
	}
}

private ITelephony getITelephony() {
	//Context.TELEPHONY_SERVICE对应字符串"phone"
	return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
}

上面的代码较为简单,唯一值得关注的是找到binder通信对应的服务提供者。
实际上我们在之前的blog中已经提到过了,在PhoneApp启动时会创建PhoneGlobals,而PhoneGlobals会创建PhoneInterfaceManager:

3 PhoneInterfaceManager

.......
phoneMgr = PhoneInterfaceManager.init(this, PhoneFactory.getDefaultPhone());
......

我们来看看PhoneInterfaceManager的定义:

//可以看到PhoneInterfaceManager继承ITelephony.Stub,与前面呼应起来了
public class PhoneInterfaceManager extends ITelephony.Stub {
	.............
	static PhoneInterfaceManager init(PhoneGlobals app, Phone phone) {
        synchronized (PhoneInterfaceManager.class) {
            if (sInstance == null) {
	            //创建PhoneInterfaceManager
                sInstance = new PhoneInterfaceManager(app, phone);
            } else {
                Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
            }
            return sInstance;
        }
	}

	private PhoneInterfaceManager(PhoneGlobals app, Phone phone) {
		...........
		publish();
	}

	private void publish() {
        if (DBG) log("publish: " + this);
		//publish服务名为"phone",与前面对应起来了
        ServiceManager.addService("phone", this);
    }
    ...........
}

至此,我们知道了TelephonyManager通过Binder通信调用的实际上是PhoneInterfaceManager中的接口。

@Override
public void setDataEnabled(int subId, boolean enable) {
	//检查权限
	enforceModifyPermission();
	//利用subId映射得到phoneId
	int phoneId = mSubscriptionController.getPhoneId(subId);
	if (DBG) log("getDataEnabled: subId=" + subId + " phoneId=" + phoneId);
	//根据phoneId得到PhoneFactory中记录的phone对象
	Phone phone = PhoneFactory.getPhone(phoneId);
	if (phone != null) {
		if (DBG) log("setDataEnabled: subId=" + subId + " enable=" + enable);
		//调用phone对象的setDataEnabled方法
		phone.setDataEnabled(enable);
	} else {
		loge("setDataEnabled: no phone for subId=" + subId);
	}
}

4 GsmCdmaPhone
与之前的版本不一样,Android 7.0中新增了GsmCdmaPhone对象,并且将Phone变成了一个抽象类,新增了PhoneInternaInterface接口:

public abstract class Phone extends Handler implements PhoneInternalInterface {
	...........
}
public class GsmCdmaPhone extends Phone {
	...........
}

这种继承结构的变化带来的影响,自己目前也还没有深入研究,今后有流程涉及时,再做进一步的分析。
我们目前还是关注数据拨号流程:

@Override
public void setDataEnabled(boolean enable) {
	mDcTracker.setDataEnabled(enable);
}

可以看到在拨号流程中,GsmCdmaPhone的工作很简单,直接调用DcTracker的接口即可。

5 DcTracker
在Android 7.0中,去掉了DcTrackerBase对象,DcTracker直接继承Handler。

public class DcTracker extends Handler {
	.......
	public void setDataEnabled(boolean enable) {
		Message msg = obtainMessage(DctConstants.CMD_SET_USER_DATA_ENABLE);
        msg.arg1 = enable ? 1 : 0;
        if (DBG) log("setDataEnabled: sendMessage: enable=" + enable);
        //发送消息给自己,将调用onSetUserDataEnabled进行处理
        sendMessage(msg);
    }
    ........
    private void onSetUserDataEnabled(boolean enabled) {
        synchronized (mDataEnabledLock) {
	        //新设定的状态,与旧状态不一样时,才需要继续处理
            if (mUserDataEnabled != enabled) {
                mUserDataEnabled = enabled;

                // 更新数据库,注意到单、双卡更新字段的区别
                if (TelephonyManager.getDefault().getSimCount() == 1) {
                    Settings.Global.putInt(mResolver, Settings.Global.MOBILE_DATA, enabled ? 1 : 0);
                } else {
                    int phoneSubId = mPhone.getSubId();
                    Settings.Global.putInt(mResolver, Settings.Global.MOBILE_DATA + phoneSubId,
                            enabled ? 1 : 0);
                }
                //根据系统属性判断终端是否允许在漫游状态使用数据业务
                if (getDataOnRoamingEnabled() == false &&
                        mPhone.getServiceState().getDataRoaming() == true) {
                    if (enabled) {
	                    //仅为不可用的APN发送通知
                        notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
                    } else {
                        notifyOffApnsOfAvailability(Phone.REASON_DATA_DISABLED);
                    }
                }

                if (enabled) {
	                //开启数据业务时,调用该函数
                    onTrySetupData(Phone.REASON_DATA_ENABLED);
                } else {
	                //关闭数据业务时,调用该函数
                    onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED);
                }
            }
        }
    }
	..............
}

onTrySetupData的内容较为简单,直接调用setupDataOnConnectableApns:

private boolean onTrySetupData(String reason) {
	if (DBG) log("onTrySetupData: reason=" + reason);
	//顾名思义,将利用可连接的APN进行拨号
	setupDataOnConnectableApns(reason);
	return true;
}

private void setupDataOnConnectableApns(String reason) {
	//这里RetryFailures.ALWAYS表示连网失败话,会一直重试
	setupDataOnConnectableApns(reason, RetryFailures.ALWAYS);
}

private void setupDataOnConnectableApns(String reason, RetryFailures retryFailures) {
	...............
	//介绍Phone拨号前的准备工作时,我们已经已经mPrioritySortedApnContexts是通过解析xml文件形成的
	for (ApnContext apnContext : mPrioritySortedApnContexts) {
		//如果apnContext之前用过,不处于Idle态(apnContext初始时处于Idle态),那么按需释放对应的数据连接,这一部分我们目前不用太关注
		......................
		//注意到apnContext的isConnectable返回true时,拨号流程才能继续下去
		if (apnContext.isConnectable()) {
			log("isConnectable() call trySetupData");
			apnContext.setReason(reason);
			//首次使用的ApnContext, waitingApns为null
			trySetupData(apnContext, waitingApns);
		}
  • 7
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
状态栏的问题是什么?为什么在7.0以上的手机上运行时感觉没有沉浸式状态栏了?如何解决这个问题? 回答: 在Android 7.0以上的手机上,出现了一个问题,即无论怎么修改状态栏背景色都没有效果,看起来不再是沉浸式的状态栏。这个问题的原因是在Android 7.0之前,状态栏的实现是通过DecorView这个类来完成的,而在7.0以上,DecorView变成了一个独立的类,并且增加了一些新的属性和方法。其中一个新的属性是mSemiTransparentStatusBarColor,这个属性可能与我们要解决的问题有关。\[1\]\[2\]\[3\] 要解决这个问题,我们可以尝试修改mSemiTransparentStatusBarColor属性的值,以达到我们想要的状态栏效果。具体的实现方法可能需要根据具体的应用程序和代码来进行调整。可以通过查看相关的文档和源代码来了解如何正确地修改这个属性。 #### 引用[.reference_title] - *1* *2* [android状态栏半透明灰色,快速解决Android7.0下沉浸式状态栏变灰的问题](https://blog.csdn.net/weixin_35792040/article/details/117493339)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [android状态栏半透明灰色,Android7.0沉浸式状态栏蒙灰问题完美解决](https://blog.csdn.net/weixin_34493827/article/details/117493345)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值