Android 4.0 framework 数据业务学习总结

2024-04-18 14:38

本文主要是介绍Android 4.0 framework 数据业务学习总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

简介

本条目用于记录本人对Android framework侧数据业务的阶段学习总结。

内容包括流程图,代码分析,BUG用例等。

第一阶段学习成果

本阶段主要注重对数据连接设置管理流程的学习,掌握数据业务的基本流程与构造。同时尝试解决部分简单BUG。

数据连接设置管理介绍

通过Setting的常规设置页面中的数据连接开关,打开/关闭数据连接业务。

数据业务设置完成后如何更新到status bar上。

开机后如何自启动数据业务。

这些过程是基本数据业务之一。

数据连接设置管理流程图

数据连接设置管理代码分析

通过Settings控制数据连接开关

Settings.java文件提供动作的入口。通过ConnectivityManager实例的setMobileDataEnable方法设置此开关。

    /**
* Invoked on each preference click in this hierarchy, overrides
* PreferenceActivity's implementation.  Used to make sure we track the
* preference click events.
*/
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
/** TODO: Refactor and get rid of the if's using subclasses */
...
} else if (preference == mButtonDataEnabled) {
if (DBG) log("onPreferenceTreeClick: preference == mButtonDataEnabled.");
ConnectivityManager cm =
(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
cm.setMobileDataEnabled(mButtonDataEnabled.isChecked());
return true;
...
}

这个方法实际上调用的是ConnectivityService实例的setMobileDataEnable方法。

     /**
* @see ConnectivityManager#setMobileDataEnabled(boolean)
*/
public void setMobileDataEnabled(boolean enabled) {
enforceChangePermission();
if (DBG) log("setMobileDataEnabled(" + enabled + ")");
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_MOBILE_DATA,
(enabled ? ENABLED : DISABLED), 0));
}

通过Handler回调机制,我们在handleMessage对消息EVENT_SET_MOBILE_DATA进行处理。

走到了handleSetMobileData方法中。

     private void handleSetMobileData(boolean enabled) {
if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {
if (VDBG) {
log(mNetTrackers[ConnectivityManager.TYPE_MOBILE].toString() + enabled);
}
mNetTrackers[ConnectivityManager.TYPE_MOBILE].setUserDataEnable(enabled);
}
if (mNetTrackers[ConnectivityManager.TYPE_WIMAX] != null) {
if (VDBG) {
log(mNetTrackers[ConnectivityManager.TYPE_WIMAX].toString() + enabled);
}
mNetTrackers[ConnectivityManager.TYPE_WIMAX].setUserDataEnable(enabled);
}
}

该方法根据传入的动作(enable or disable),设置对应的networkStateTracker标志位状态。

起作用的方法也就是MobileDataStateTracker实例的setUserDataEnable方法。

     @Override
public void setUserDataEnable(boolean enabled) {
if (DBG) log("setUserDataEnable: E enabled=" + enabled);
final AsyncChannel channel = mDataConnectionTrackerAc;
if (channel != null) {
channel.sendMessage(DctConstants.CMD_SET_USER_DATA_ENABLE,
enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
mUserDataEnabled = enabled;
}
if (VDBG) log("setUserDataEnable: X enabled=" + enabled);
}

同样使用了Handler的回调机制,通过回调之后的消息

DctConstants.CMD_SET_USER_DATA_ENABLE

找到对应的在DataConnectionTracker里的Handler处理方法线程。

             case DctConstants.CMD_SET_USER_DATA_ENABLE: {
final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
if (DBG) log("CMD_SET_USER_DATA_ENABLE enabled=" + enabled);
onSetUserDataEnabled(enabled);
break;
}

即回调至方法onSetUserDataEnabled()中

     protected void onSetUserDataEnabled(boolean enabled) {
synchronized (mDataEnabledLock) {
final boolean prevEnabled = getAnyDataEnabled();
if (mUserDataEnabled != enabled) {
mUserDataEnabled = enabled;
Settings.Global.putInt(mPhone.getContext().getContentResolver(),
Settings.Global.MOBILE_DATA, enabled ? 1 : 0);
if (getDataOnRoamingEnabled() == false &&
mPhone.getServiceState().getRoaming() == true) {
if (enabled) {
notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
} else {
notifyOffApnsOfAvailability(Phone.REASON_DATA_DISABLED);
}
}
if (prevEnabled != getAnyDataEnabled()) {
if (!prevEnabled) {
resetAllRetryCounts();
onTrySetupData(Phone.REASON_DATA_ENABLED);
} else {
onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
}
}
}
}
}

这个方法主要可分成两部分理解。

传进来的enable动作影响了mUserDataEnable,同时也作为数据存放到数据库中。

如果这次动作传入有效(prevEnable与AnyDataEnable之间发生变化),

当enabled 为 TRUE(即开启数据连接开关) ,那么就有一次成功的建立链接动作(调用resetAllRetryCounts重置计数器,然后通过onTrySetupData建立链接)。

当enabled 为 FALSE (即关闭数据连接开关),那么就清空所有的链接(调用onCleanUpAllConnections)。


如果成功的话,最终这个动作走入gsmDataConnectionTracker的setupData方法。

通过bringUp方法建立数据链接。

注意带上了what=DctConstants.EVENT_DATA_SETUP_COMPLETE消息的msg方法是当该动作完成后才被回调。具体的建立链接动作进入bringUp中查看。

        Message msg = obtainMessage();
msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
msg.obj = apnContext;
dc.bringUp(msg, apn);
if (DBG) log("setupData: initing!");
return true;

进到DataConnection.java中,发现这个方法仍然是个回调处理机制。

     /**
* Bring up a connection to the apn and return an AsyncResult in onCompletedMsg.
* Used for cellular networks that use Acesss Point Names (APN) such
* as GSM networks.
*
* @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
*        With AsyncResult.userObj set to the original msg.obj,
*        AsyncResult.result = FailCause and AsyncResult.exception = Exception().
* @param apn is the Access Point Name to bring up a connection to
*/
public void bringUp(Message onCompletedMsg, ApnSetting apn) {
sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(apn, onCompletedMsg)));
}

假设这是个建立链接的过程,而不是断开链接的过程。那么实际上走入InactiveState这个case中。

         @Override
public boolean processMessage(Message msg) {
boolean retVal;
switch (msg.what) {
...
case EVENT_CONNECT:
ConnectionParams cp = (ConnectionParams) msg.obj;
cp.tag = mTag;
if (DBG) {
log("DcInactiveState msg.what=EVENT_CONNECT." + "RefCount = "
+ mRefCount);
}
mRefCount = 1;
onConnect(cp);
transitionTo(mActivatingState);
retVal = HANDLED;
break;
}
}

除了上述状态外,EVENT_CONNECT时数据链路的状态还可能是ActivatingState、ActiveState以及DisconnectingState。关于这几种状态之间的转换关系,可以由下图表示(图源:再论android 2.2数据连接过程 - Armily's Tech Blog - 博客频道 - CSDN.NET):

之后进入GsmDataConnection的onCreate()方法。

     /**
* Begin setting up a data connection, calls setupDataCall
* and the ConnectionParams will be returned with the
* EVENT_SETUP_DATA_CONNECTION_DONE AsyncResul.userObj.
*
* @param cp is the connection parameters
*/
@Override
protected
void onConnect(ConnectionParams cp) {
mApn = cp.apn;
if (DBG) log("Connecting to carrier: '" + mApn.carrier
+ "' APN: '" + mApn.apn
+ "' proxy: '" + mApn.proxy + "' port: '" + mApn.port);
createTime = -1;
lastFailTime = -1;
lastFailCause = FailCause.NONE;
// msg.obj will be returned in AsyncResult.userObj;
Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
msg.obj = cp;
int authType = mApn.authType;
if (authType == -1) {
authType = TextUtils.isEmpty(mApn.user) ? RILConstants.SETUP_DATA_AUTH_NONE
: RILConstants.SETUP_DATA_AUTH_PAP_CHAP;
}
String protocol;
if (phone.getServiceState().getRoaming()) {
protocol = mApn.roamingProtocol;
} else {
protocol = mApn.protocol;
}
phone.mCM.setupDataCall(
Integer.toString(getRilRadioTechnology(RILConstants.SETUP_DATA_TECH_GSM)),
Integer.toString(mProfileId),
mApn.apn, mApn.user, mApn.password,
Integer.toString(authType),
protocol, msg);
}

看到最后仍然是通过ril的方法setupDataCall以socket方式发送下去。

     public void
setupDataCall(String radioTechnology, String profile, String apn,
String user, String password, String authType, String protocol,
Message result) {
RILRequest rr
= RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result);
rr.mp.writeInt(7);
rr.mp.writeString(radioTechnology);
rr.mp.writeString(profile);
rr.mp.writeString(apn);
rr.mp.writeString(user);
rr.mp.writeString(password);
rr.mp.writeString(authType);
rr.mp.writeString(protocol);
if (RILJ_LOGD) riljLog(rr.serialString() + "> "
+ requestToString(rr.mRequest) + " " + radioTechnology + " "
+ profile + " " + apn + " " + user + " "
+ password + " " + authType + " " + protocol);
send(rr);
}

这个就是建立数据链接,从app到ril的流程。

数据连接设置成功后的消息反馈机制

接下来关注消息EVENT_DATA_SETUP_COMPLETE的回调,EVENT_DATA_SETUP_COMPLETE消息在数据连接setupData动作完成时被捕获。

其作用是对本次连接的结果进行处理。

在handleMessage@DataConnectionTracker.java中

             case DctConstants.EVENT_DATA_SETUP_COMPLETE:
mCidActive = msg.arg1;
onDataSetupComplete((AsyncResult) msg.obj);
break;

即回调的处理方法是onDataSetupComplete()@GsmDataConnectionTracker.java。

该方法针对可能出现的连接失效情况进行异常处理或重连。

如果连接建立成功,同时其他设置皆完善的情况下,最后会以notify的方式通知Phone实例数据业务建立成功。

                DataConnection dc = apnContext.getDataConnection();
ApnSetting apn = apnContext.getApnSetting();
if (DBG) {
log("onDataSetupComplete: success apn=" + (apn == null ? "unknown" : apn.apn));
}
if (apn != null && apn.proxy != null && apn.proxy.length() != 0) {
try {
String port = apn.port;
if (TextUtils.isEmpty(port)) port = "8080";
ProxyProperties proxy = new ProxyProperties(apn.proxy,
Integer.parseInt(port), null);
dcac.setLinkPropertiesHttpProxySync(proxy);
} catch (NumberFormatException e) {
loge("onDataSetupComplete: NumberFormatException making ProxyProperties (" +
apn.port + "): " + e);
}
}
// everything is setup
if(TextUtils.equals(apnContext.getApnType(),PhoneConstants.APN_TYPE_DEFAULT)) {
SystemProperties.set("gsm.defaultpdpcontext.active", "true");
if (canSetPreferApn && mPreferredApn == null) {
if (DBG) log("onDataSetupComplete: PREFERED APN is null");
mPreferredApn = apn;
if (mPreferredApn != null) {
setPreferredApn(mPreferredApn.id);
}
}
} else {
SystemProperties.set("gsm.defaultpdpcontext.active", "false");
}
notifyDefaultData(apnContext);

这个方法最终调用DefaultPhoneNotifier.java实例的doNotifyDataConnection()方法。

如果上层在NetworkController.java里已经注册过对应的消息。

     /**
* Construct this controller object and register for updates.
*/
public NetworkController(Context context) {
...
// telephony
mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
mPhone.listen(mPhoneStateListener,
PhoneStateListener.LISTEN_SERVICE_STATE
| PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
| PhoneStateListener.LISTEN_CALL_STATE
| PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
| PhoneStateListener.LISTEN_DATA_ACTIVITY);
}

那么该方法用于相应这些注册消息。

        try {
mRegistry.notifyDataConnection(
convertDataState(state),
sender.isDataConnectivityPossible(tmpApnType[0]), reason,
sender.getActiveApnHost(tmpApnType[0]),
apnType,
linkProperties,
linkCapabilities,
((telephony!=null) ? telephony.getNetworkType() :
TelephonyManager.NETWORK_TYPE_UNKNOWN),
roaming);
} catch (RemoteException ex) {
// system process is dead
}

找到对应实现方法处。

在TelephonyRegistry.java实例的notfiyDataConnection()方法中,有

            if (modified) {
if (DBG) {
Slog.d(TAG, "onDataConnectionStateChanged(" + mDataConnectionState
+ ", " + mDataConnectionNetworkType + ")");
}
for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
try {
r.callback.onDataConnectionStateChanged(mDataConnectionState,
mDataConnectionNetworkType);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
}
}
handleRemoveListLocked();
}
}
broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
apnType, linkProperties, linkCapabilities, roaming);

注意mRecords.events实际上就是之前在NetworkController.java中注册的消息。如果这些消息存在,同时LISTEN_DATA_CONNECTION_STATE状态正常,那么就回调各自对应的onDataConnectionStateChanged()方法。

这是典型的OBSERVER模式。

最终这个回调的实现在消息所注册的类NetworkController.java中。

               @Override
public void onDataConnectionStateChanged(int state, int networkType) {
if (DEBUG) {
Slog.d(TAG, "onDataConnectionStateChanged: state=" + state
+ " type=" + networkType);
}
mDataState = state;
mDataNetType = networkType;
updateDataNetType();
updateDataIcon();
refreshViews();
}

该方法实现了status bar上对于数据业务图标的显示和更新。

开机后系统自动启动数据连接业务

如果关机前用户已经开启了“数据连接”业务,且未进入飞行模式。

那么重新开机后,系统应该自动进入“数据连接”的连接态。

具体流程如下:

注意到GsmDataConnectionTracker.java中会监听已注册的关于数据连接相关的消息。在handler中进行处理:

         switch (msg.what) {
case EVENT_RECORDS_LOADED:
onRecordsLoaded();
break;
case EVENT_DATA_CONNECTION_DETACHED:
onDataConnectionDetached();
break;
case EVENT_DATA_CONNECTION_ATTACHED:
onDataConnectionAttached();
break;
case EVENT_DATA_STATE_CHANGED:
onDataStateChanged((AsyncResult) msg.obj);
break;
case EVENT_POLL_PDP:
onPollPdp();
break;
case EVENT_DO_RECOVERY:
doRecovery();
break;
case EVENT_APN_CHANGED:
onApnChanged();
break;
case EVENT_PS_RESTRICT_ENABLED:
/**
* We don't need to explicitly to tear down the PDP context
* when PS restricted is enabled. The base band will deactive
* PDP context and notify us with PDP_CONTEXT_CHANGED.
* But we should stop the network polling and prevent reset PDP.
*/
if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
stopNetStatPoll();
stopDataStallAlarm();
mIsPsRestricted = true;
break;
case EVENT_PS_RESTRICT_DISABLED:
/**
* When PS restrict is removed, we need setup PDP connection if
* PDP connection is down.
*/
if (DBG) log("EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
mIsPsRestricted  = false;
if (isConnected()) {
startNetStatPoll();
startDataStallAlarm();
} else {
// TODO: Should all PDN states be checked to fail?
if (mState == State.FAILED) {
cleanUpAllConnections(false, Phone.REASON_PS_RESTRICT_ENABLED);
resetAllRetryCounts();
mReregisterOnReconnectFailure = false;
}
trySetupData(Phone.REASON_PS_RESTRICT_ENABLED, Phone.APN_TYPE_DEFAULT);
}
break;
case EVENT_TRY_SETUP_DATA:
onTrySetupData(msg);
break;
case EVENT_CLEAN_UP_CONNECTION:
boolean tearDown = (msg.arg1 == 0) ? false : true;
if (DBG) log("EVENT_CLEAN_UP_CONNECTION tearDown=" + tearDown);
if (msg.obj instanceof ApnContext) {
cleanUpConnection(tearDown, (ApnContext)msg.obj);
} else {
loge("EVENT_CLEAN_UP_CONNECTION request w/o apn context");
}
break;
case EVENT_RAT_CHANGED:
onRatChanged();
break;
default:
// handle the message in the super class DataConnectionTracker
super.handleMessage(msg);
break;
}

这些消息被捕获到时,会回调对应的方法。以sim卡成功被Load为例。

将会回调onRecordsLoaded()方法。

     private void onRecordsLoaded() {
if (DBG) log("onRecordsLoaded: createAllApnList");
createAllApnList();
if (mPhone.mCM.getRadioState().isOn()) {
if (DBG) log("onRecordsLoaded: notifying data availability");
notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);
}
setupDataOnReadyApns(Phone.REASON_SIM_LOADED);
}

即将相应的reason送给了SetupDataOnReadyApn()方法。

与之类似的,其他消息被回调时,最终都会调用该方法来完成消息的传递。

SetupDataOnReadyApn()方法在确认apnContext无误之后,会调用之前提到的trySetupData()方法。

         // Only check for default APN state
for (ApnContext apnContext :
getPrioritySortedApnContextList().toArray(new ApnContext[0])) {
if (apnContext.getState() == State.FAILED) {
// By this time, alarms for all failed Apns
// should be stopped if any.
// Make sure to set the state back to IDLE
// so that setup data can happen.
apnContext.setState(State.IDLE);
}
if (apnContext.isReady()) {
if (apnContext.getState() == State.IDLE ||
(apnContext.getState() == State.CONNECTED &&
apnContext.getDataConnectionAc().getPartialSuccessStatusSync())) {
apnContext.setReason(reason);
trySetupData(apnContext);
}
}
}

最终与Settings设置的方式,将对应的消息以socket方式发给rild。

完成开机后即可以自动设置“数据连接”的动作。

转载自:点击打开链接http://blog.csdn.net/guiyu_1985/article/details/8983609


这篇关于Android 4.0 framework 数据业务学习总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/915063

相关文章

SQL中JOIN操作的条件使用总结与实践

《SQL中JOIN操作的条件使用总结与实践》在SQL查询中,JOIN操作是多表关联的核心工具,本文将从原理,场景和最佳实践三个方面总结JOIN条件的使用规则,希望可以帮助开发者精准控制查询逻辑... 目录一、ON与WHERE的本质区别二、场景化条件使用规则三、最佳实践建议1.优先使用ON条件2.WHERE用

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

Nginx Location映射规则总结归纳与最佳实践

《NginxLocation映射规则总结归纳与最佳实践》Nginx的location指令是配置请求路由的核心机制,其匹配规则直接影响请求的处理流程,下面给大家介绍NginxLocation映射规则... 目录一、Location匹配规则与优先级1. 匹配模式2. 优先级顺序3. 匹配示例二、Proxy_pa

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

MySQL基本查询示例总结

《MySQL基本查询示例总结》:本文主要介绍MySQL基本查询示例总结,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Create插入替换Retrieve(读取)select(确定列)where条件(确定行)null查询order by语句li

重新对Java的类加载器的学习方式

《重新对Java的类加载器的学习方式》:本文主要介绍重新对Java的类加载器的学习方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍1.1、简介1.2、符号引用和直接引用1、符号引用2、直接引用3、符号转直接的过程2、加载流程3、类加载的分类3.1、显示

Android NDK版本迭代与FFmpeg交叉编译完全指南

《AndroidNDK版本迭代与FFmpeg交叉编译完全指南》在Android开发中,使用NDK进行原生代码开发是一项常见需求,特别是当我们需要集成FFmpeg这样的多媒体处理库时,本文将深入分析A... 目录一、android NDK版本迭代分界线二、FFmpeg交叉编译关键注意事项三、完整编译脚本示例四

Android与iOS设备MAC地址生成原理及Java实现详解

《Android与iOS设备MAC地址生成原理及Java实现详解》在无线网络通信中,MAC(MediaAccessControl)地址是设备的唯一网络标识符,本文主要介绍了Android与iOS设备M... 目录引言1. MAC地址基础1.1 MAC地址的组成1.2 MAC地址的分类2. android与I

Linux区分SSD和机械硬盘的方法总结

《Linux区分SSD和机械硬盘的方法总结》在Linux系统管理中,了解存储设备的类型和特性是至关重要的,不同的存储介质(如固态硬盘SSD和机械硬盘HDD)在性能、可靠性和适用场景上有着显著差异,本文... 目录一、lsblk 命令简介基本用法二、识别磁盘类型的关键参数:ROTA查询 ROTA 参数ROTA

Android 实现一个隐私弹窗功能

《Android实现一个隐私弹窗功能》:本文主要介绍Android实现一个隐私弹窗功能,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 效果图如下:1. 设置同意、退出、点击用户协议、点击隐私协议的函数参数2. 《用户协议》、《隐私政策》设置成可点击的,且颜色要区分出来res/l