【Android架构Telephony篇】之数据业务(二)
来源:互联网 发布:药品网络经营许可证 编辑:程序博客网 时间:2024/06/05 05:21
Android: 4.4.4Desktop: Ubuntu 15.04
这篇梳理Telephony数据业务的JAVA方法走向。
http://write.blog.csdn.net/postedit/49719897
更新日期:2016-12-12
涉及到的文件见:
【Android架构Telephony篇】之数据业务(一)
三、数据业务的Framework层
点击流量开启/关闭按钮执行的相关代码如下:
\packages\apps\settings\src\com\android\settings\DataUsageSummary.javapublic class DataUsageSummary extends Fragment {private static final String TAG = "DataUsage"; private static final boolean LOGD = true;private Switch mDataEnabled;private View mDataEnabledView;private ConnectivityManager mConnService;mDataEnabled = new Switch(inflater.getContext()); mDataEnabledView = inflatePreference(inflater, mNetworkSwitches, mDataEnabled); mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener);/** * Update body content based on current tab. Loads * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and * binds them to visible controls. */ private void updateBody() { final String currentTab = mTabHost.getCurrentTabTag(); final boolean isOwner = ActivityManager.getCurrentUser() == UserHandle.USER_OWNER; if (currentTab == null) { Log.w(TAG, "no tab selected; hiding body"); mListView.setVisibility(View.GONE); return; } else { mListView.setVisibility(View.VISIBLE); } final boolean tabChanged = !currentTab.equals(mCurrentTab); mCurrentTab = currentTab; if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab); mDataEnabledView.setVisibility(isOwner ? View.VISIBLE : View.GONE); // TODO: remove mobile tabs when SIM isn't ready final TelephonyManager tele = TelephonyManager.from(context); if (TAB_MOBILE.equals(currentTab)) {// 确保选中的是MOBILE/* R.string.data_usage_enable_mobile: 移动数据 */ setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile); setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit); mTemplate = buildTemplateMobileAll(getActiveSubscriberId(context)); } }private void setMobileDataEnabled(boolean enabled) { if (LOGD) Log.d(TAG, "setMobileDataEnabled()"); mConnService.setMobileDataEnabled(enabled); mMobileDataEnabled = enabled; updatePolicy(false); }private OnCheckedChangeListener mDataEnabledListener = new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (mBinding) return; final boolean dataEnabled = isChecked; final String currentTab = mCurrentTab; if (TAB_MOBILE.equals(currentTab)) { if (dataEnabled) { setMobileDataEnabled(true); } else { // disabling data; show confirmation dialog which eventually // calls setMobileDataEnabled() once user confirms. ConfirmDataDisableFragment.show(DataUsageSummary.this); } } updatePolicy(false); } };}一旦选中数据流量选项卡,updateBody()方法得到执行,【开启/关闭】的按钮等就呈现在界面上。
OnCheckedChangeListener()监听器在点击【开启/关闭】按钮使按钮状态发生改变后调用,相应的就打开或关闭数据业务。
起作用的方法是setMobileDataEnabled(true)并最终调用了ConnectivityManager类的setMobileDataEnabled()方法。
ConnectivityManager类作用在注释里说的很清楚,我们专注于方法调用流程:
\frameworks\base\core\java\android\net\ConnectivityManager.java/** * Class that answers queries about the state of network connectivity. It also * notifies applications when network connectivity changes. Get an instance * of this class by calling * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context.CONNECTIVITY_SERVICE)}. * <p> * The primary responsibilities of this class are to: * <ol> * <li>Monitor network connections (Wi-Fi, GPRS, UMTS, etc.)</li> * <li>Send broadcast intents when network connectivity changes</li> * <li>Attempt to "fail over" to another network when connectivity to a network * is lost</li> * <li>Provide an API that allows applications to query the coarse-grained or fine-grained * state of the available networks</li> * </ol> */public class ConnectivityManager { private static final String TAG = "ConnectivityManager";private final IConnectivityManager mService;/* return Whether mobile data is enabled. */ public boolean getMobileDataEnabled() { try { return mService.getMobileDataEnabled(); } catch (RemoteException e) { return true; } } /* param enabled Whether the user wants the mobile data connection used */ public void setMobileDataEnabled(boolean enabled) { try { mService.setMobileDataEnabled(enabled); } catch (RemoteException e) { } }}这里,ConnectivityManager类又调到了IConnectivityManager的setMobileDataEnabled()方法,但是IConnectivityManager是一个接口,用于进程间通信(IPC)、远程函数调用(RPC):
\frameworks\base\core\java\android\net\IConnectivityManager.aidl/** * 类或API是否开放,通过doc的注释{@hide}来控制的 * 比如,android.media.Metadata这个类定义Metadata类之前有/**{@hide}*/就表明android没有公开该类,所以Metadata类被定义为了非公开类——在android应用程序中无法直接访问的类。 *//** {@hide} */interface IConnectivityManager{ boolean getMobileDataEnabled(); void setMobileDataEnabled(boolean enabled);}
经验: 通常来说,XxxManager用来管理服务,而服务的具体实现在XxxService中。
实现该接口setMobileDataEnabled()方法的类是ConnectivityService:
\frameworks\base\services\java\com\android\server\ConnectivityService.javapublic class ConnectivityService extends IConnectivityManager.Stub { private static final String TAG = "ConnectivityService";public static final int MAX_NETWORK_TYPE = 14;/** Handler used for internal events. */ private InternalHandler mHandler;/** * Sometimes we want to refer to the individual network state * trackers separately, and sometimes we just want to treat them * abstractly. */ private NetworkStateTracker mNetTrackers[];mNetTrackers = new NetworkStateTracker[ ConnectivityManager.MAX_NETWORK_TYPE+1];/** * @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)); } private class InternalHandler extends Handler { public InternalHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { NetworkInfo info; switch (msg.what) { /* .... */ case EVENT_SET_MOBILE_DATA: { boolean enabled = (msg.arg1 == ENABLED); handleSetMobileData(enabled); break; } } } }private void handleSetMobileData(boolean enabled) { if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) { mNetTrackers[ConnectivityManager.TYPE_MOBILE].setUserDataEnable(enabled); } if (mNetTrackers[ConnectivityManager.TYPE_WIMAX] != null) { mNetTrackers[ConnectivityManager.TYPE_WIMAX].setUserDataEnable(enabled); } }}该类通过扩展Handler,实现信息的发送和处理,于是现在绕到了NetworkStateTracker类的setUserDataEnable()方法。
在这里,代码定义了一个NetworkStateTracker类型的数组:
mNetTrackers = new NetworkStateTracker[ ConnectivityManager.MAX_NETWORK_TYPE+1];可知NetworkStateTracker除了Mobile,还要服务于Wi-Fi、4G等网络。
但是,NetworkStateTracker又是一个接口:
\frameworks\base\core\java\android\net\NetworkStateTracker.java/** * Interface provides the {@link com.android.server.ConnectivityService} * with three services. Events to the ConnectivityService when * changes occur, an API for controlling the network and storage * for network specific information. */public interface NetworkStateTracker {/** * User control of data connection through this network, typically persisted * internally. */ public void setUserDataEnable(boolean enabled);}BaseNetworkStateTracker类以抽象类的形式implements该NetworkStateTracker接口类,为的是屏蔽具体网络的细节—这不就是面向对象存在的一大目嘛。
来看BaseNetworkStateTracker:
\frameworks\base\core\java\android\net\BaseNetworkStateTracker.java/** * Interface to control and observe state of a specific network, hiding * network-specific details from {@link ConnectivityManager}. Surfaces events * through the registered {@link Handler} to enable {@link ConnectivityManager} * to respond to state changes over time. */public abstract class BaseNetworkStateTracker implements NetworkStateTracker {@Override public void setUserDataEnable(boolean enabled) { // Base tracker doesn't handle enabled flags }}
setUserDataEnable()方法的最终实现实在MobileDataStateTracker类中(继承,用自己的方式实现父类的方法).但是,它也仅仅采用异步的方式,把改变通知其他线程而已:
\frameworks\base\core\java\android\net\MobileDataStateTracker.java/** * Track the state of mobile data connectivity. This is done by * receiving broadcast intents from the Phone process whenever * the state of data connectivity changes. */public class MobileDataStateTracker extends BaseNetworkStateTracker {private AsyncChannel mDataConnectionTrackerAc; @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); }}
channel.sendMessage(DctConstants.CMD_SET_USER_DATA_ENABLE,enabled ? DctConstants.ENABLED : DctConstants.DISABLED)通知走向哪里了呢?继续看:
\frameworks\base\telephony\java\com\android\internal\telephony\DctConstants.java (h:public class DctConstants {/***** Event Codes *****/ public static final int BASE = Protocol.BASE_DATA_CONNECTION_TRACKER;public static final int CMD_SET_USER_DATA_ENABLE = BASE + 30;}\frameworks\opt\telephony\src\java\com\android\internal\telephony\dataconnection\DcTrackerBase.javapublic abstract class DcTrackerBase extends Handler {@Override public void handleMessage(Message msg) { switch (msg.what) { 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; }}}protected void onSetUserDataEnabled(boolean enabled) { synchronized (mDataEnabledLock) { final boolean prevEnabled = getAnyDataEnabled(); if (mUserDataEnabled != enabled) { mUserDataEnabled = enabled; /* 把状态更新到本地数据库中 * 因为有的地方是通过读取数据库中MOBILE_DATA属性来确定移动网络是否开启 */ 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) { onTrySetupData(Phone.REASON_DATA_ENABLED); } else { onCleanUpAllConnections(Phone.REASON_DATA_DISABLED); } } } } } // abstract methods protected abstract boolean onTrySetupData(String reason);}前面九曲八弯的,在源码分析时遇到Interface总是费点周折,,,这里DcTrackerBase才是干实事的,但是在onSetUserDataEnabled()也只干了一部分:
1、如果数据业务的状态(启动/关闭)发生了变化(mUserDataEnabled != enabled),更新mUserDataEnabled状态;
2、把新状态写到Settings里;
3、把状态改变的消息通知其他所有相关工作人员;
4、继续往下走onTrySetupData();,直到完成硬件操作。
但是onTrySetupData()是抽象方法,由不得再去周折去搜寻了:
\frameworks\opt\telephony\src\java\com\android\internal\telephony\dataconnection\DcTracker.javapublic final class DcTracker extends DcTrackerBase {// \frameworks\opt\telephony\src\java\com\android\internal\telephony\dataconnection\ApnContext.javapublic boolean isConnectable() { return isReady() && ((mState == DctConstants.State.IDLE) || (mState == DctConstants.State.SCANNING) || (mState == DctConstants.State.RETRYING) || (mState == DctConstants.State.FAILED)); }private boolean isDataAllowed(ApnContext apnContext) { return apnContext.isReady() && isDataAllowed(); }/** * Report on whether data connectivity is enabled for any APN. * @return {@code false} if data connectivity has been explicitly disabled, * {@code true} otherwise. */ @Override public boolean getAnyDataEnabled() { synchronized (mDataEnabledLock) { if (!(mInternalDataEnabled && mUserDataEnabled && sPolicyDataEnabled)) return false; for (ApnContext apnContext : mApnContexts.values()) { // Make sure we don't have a context that is going down // and is explicitly disabled. if (isDataAllowed(apnContext)) { return true; } } return false; } }/* DcTrackerBase类的方法,这里放在这里 */protected boolean isEmergency() { final boolean result; synchronized (mDataEnabledLock) { result = mPhone.isInEcm() || mPhone.isInEmergencyCall(); } log("isEmergency: result=" + result); return result; } @Override // TODO: We shouldnt need this. protected boolean onTrySetupData(String reason) { if (DBG) log("onTrySetupData: reason=" + reason); setupDataOnConnectableApns(reason); return true; } protected boolean onTrySetupData(ApnContext apnContext) { if (DBG) log("onTrySetupData: apnContext=" + apnContext); return trySetupData(apnContext); }private boolean trySetupData(ApnContext apnContext) { if (apnContext.isConnectable() && isDataAllowed(apnContext) && getAnyDataEnabled() && !isEmergency()) { if (apnContext.getState() == DctConstants.State.FAILED) { if (DBG) log("trySetupData: make a FAILED ApnContext IDLE so its reusable"); apnContext.setState(DctConstants.State.IDLE); } int radioTech = mPhone.getServiceState().getRilDataRadioTechnology(); if (apnContext.getState() == DctConstants.State.IDLE) { ArrayList<ApnSetting> waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech); if (waitingApns.isEmpty()) { notifyNoData(DcFailCause.MISSING_UNKNOWN_APN, apnContext); notifyOffApnsOfAvailability(apnContext.getReason()); if (DBG) log("trySetupData: X No APN found retValue=false"); return false; } else { apnContext.setWaitingApns(waitingApns); if (DBG) { log ("trySetupData: Create from mAllApnSettings : " + apnListToString(mAllApnSettings)); } } }/* 建立连接 */ boolean retValue = setupData(apnContext, radioTech); notifyOffApnsOfAvailability(apnContext.getReason()); if (DBG) log("trySetupData: X retValue=" + retValue); return retValue; } else {if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT) && apnContext.isConnectable()) { mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType()); } notifyOffApnsOfAvailability(apnContext.getReason()); if (DBG) log ("trySetupData: X apnContext not 'ready' retValue=false"); return false; } }}数据业务建条件立前是需要检查是否满足需要条件的,这里进行了四个有效性检查:
1、isConnectable():判断当前APN是否已经被激活;
2、isDataAllowed():判断是否已经ATTACH成功,SIM是否初始化完毕,当前手机服务是否支持,漫游下是否允许上网等;
3、getAnyDataEnabled():判断数据允许的开关是否打开;
4、isEmergency():当前APN是否为紧急APN。
以上四个条件同时满足以后,就调用setupData()进行数据连接:
public final class DcTracker extends DcTrackerBase {private boolean setupData(ApnContext apnContext, int radioTech) { ApnSetting apnSetting; DcAsyncChannel dcac; Message msg = obtainMessage(); msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE; msg.obj = apnContext; dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech, msg); if (DBG) log("setupData: initing!"); return true; }}但是这里并没有实行具体的连接动作,而是调用DcAsyncChannel类的bringUp()方法:
\frameworks\opt\telephony\src\java\com\android\internal\telephony\dataconnection\DcAsyncChannel.java (h:public class DcAsyncChannel extends AsyncChannel {/** * 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. */ public void bringUp(ApnContext apnContext, int initialMaxRetry, int profileId, int rilRadioTechnology, Message onCompletedMsg) {/* \frameworks\opt\telephony\src\java\com\android\internal\telephony\dataconnection\DataConnection.java的static内部类 * Used internally for saving connecting parameters * static class ConnectionParams {} */ sendMessage(DataConnection.EVENT_CONNECT, new ConnectionParams(apnContext, initialMaxRetry, profileId, rilRadioTechnology, onCompletedMsg)); }}
消息发往状态机类DataConnection类处理:
/** * DataConnection StateMachine. */ \frameworks\opt\telephony\src\java\com\android\internal\telephony\dataconnection\DataConnection.java public final class DataConnection extends StateMachine { /* 之前提到过,它封装了之前提到的参数 */ static class ConnectionParams { } /** * Begin setting up a data connection, calls setupDataCall */ private void onConnect(ConnectionParams cp) {/** onConnect: carrier='China Unicom 3G' APN='3gnet' proxy='null' port='80' */log("onConnect: carrier='" + mApnSetting.carrier+ "' APN='" + mApnSetting.apn+ "' proxy='" + mApnSetting.proxy + "' port='" + mApnSetting.port + "'");mPhone.mCi.setupDataCall( Integer.toString(cp.mRilRat + 2), Integer.toString(cp.mProfileId), mApnSetting.apn, mApnSetting.user, mApnSetting.password, Integer.toString(authType), protocol, msg); } /** * Initialize connection, this will fail if the * apnSettings are not compatible. * * @param cp the Connection paramemters * @return true if initialization was successful. */ private boolean initConnection(ConnectionParams cp) { return true; } /** * The state machine is inactive and expects a EVENT_CONNECT. */ private class DcInactiveState extends State { @Override public boolean processMessage(Message msg) { boolean retVal; switch (msg.what) { case EVENT_CONNECT: if (DBG) log("DcInactiveState: mag.what=EVENT_CONNECT"); ConnectionParams cp = (ConnectionParams) msg.obj; if (initConnection(cp)) { onConnect(mConnectionParams); transitionTo(mActivatingState); } retVal = HANDLED; break; } } } }
在DataConnection类,数据连接建立的请求就快到了最后一站:
1、通过initConnection()对连接进行必要的初始化2、在onConnect()中调用CommandsInterface的setupDataCall()方法按照传入的参数进行工作:
又得但是,CommandsInterface还是一个接口,提供跟HAL层Ril交互的发送命令和接受命令,注册状态传递回调接口,向上层传递状态变化:
\frameworks\opt\telephony\src\java\com\android\internal\telephony\CommandsInterface.javapublic interface CommandsInterface {/** * Setup a packet data connection On successful completion, the result * message will return a {@link com.android.internal.telephony.dataconnection.DataCallResponse} * object containing the connection information. */ public void setupDataCall(String radioTechnology, String profile, String apn, String user, String password, String authType, String protocol, Message result);}setupDataCall()方法的具体实现在RIL.java文件中:
\frameworks\opt\telephony\src\java\com\android\internal\telephony\RIL.java/** * RIL implementation of the CommandsInterface. */public final class RIL extends BaseCommands implements CommandsInterface {RILSender mSender;class RILSender extends Handler implements Runnable {handleMessage(Message msg) {RILRequest rr = (RILRequest)(msg.obj);RILRequest req = null;switch (msg.what) {case EVENT_SEND:// 与rild通信socketLocalSocket s;s = mSocket; // 加入请求队列synchronized (mRequestsList) {mRequestsList.add(rr);mRequestMessagesWaiting++;}byte[] data;data = rr.mp.marshall();// 将将数据通过socket传入到rild进程中s.getOutputStream().write(dataLength);s.getOutputStream().write(data);break;}}}class RILReceiver implements Runnable {}@Override public void setupDataCall(String radioTechnology, String profile, String apn, String user, String password, String authType, String protocol, Message result) { // 获取一个RILRequest对象 RILRequest rr = RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result); rr.mParcel.writeInt(7);// 将数据打包成parcel rr.mParcel.writeString(radioTechnology); rr.mParcel.writeString(profile); rr.mParcel.writeString(apn); rr.mParcel.writeString(user); rr.mParcel.writeString(password); rr.mParcel.writeString(authType); rr.mParcel.writeString(protocol);// 将数据发送到send线程中处理 send(rr); }private void send(RILRequest rr) { Message msg; msg = mSender.obtainMessage(EVENT_SEND, rr); acquireWakeLock(); msg.sendToTarget(); }}在RIL类中,有两个内部类:负责数据发送的RILSender和负责数据接收的RILReceiver,意图很明显。
Telephony中,Framework层和HAL层通信采用的是socket机制:
out/target/product/KoolRegister/root/init.rcservice ril-daemon /system/bin/rild -l /system/lib/libreference-ril.so -- -d /dev/mux2 socket rild stream 660 root radio socket rild-debug stream 660 radio system
九曲十八弯,拐来拐去最终,数据在RILSender的线程中通过socket传递给rild进程处理。
未完,见:
【Android架构Telephony篇】之数据业务(三)
- 【Android架构Telephony篇】之数据业务(二)
- 【Android架构Telephony篇】之数据业务(一)
- 【Android架构Telephony篇】数据业务(3)RILC
- 【Android架构Telephony篇】数据业务总览
- 【Android架构Telephony篇】数据业务(1)总览
- android技术专题之二-telephony
- Android6.0 Telephony Frameworks之数据业务建立流程
- 【Android架构Telephony篇】Subscription和SubscriptionManager
- android telephony(二)
- Android telephony架构 博客链接
- Android 技术专题系列之二 -- telephony
- Android 技术专题系列之二 -- telephony
- Android 技术专题系列之二 -- telephony
- Android 技术专题系列之二 -- telephony
- Android 技术专题系列之二 -- telephony(转载)
- Android 技术专题系列之二 -- telephony
- android系统数据业务知识点总结(二)
- Android providers 解析之telephony
- 深入理解多线程
- 下一百层游戏
- 2015.11.08_06_02_sed命令
- myeclipse部署maven时,src/main/resources里面配置文件加载不到webapp下classes路径下的问题
- JDBC链接Oracle数据库的6个步骤
- 【Android架构Telephony篇】之数据业务(二)
- MySQL触发器
- Linux词汇
- 从phpMyAdmin批量导入Excel内容到MySQL
- spring配置数据源
- hdoj5124lines【贪心 or 离散化】
- ios开发-前言基础篇C语言
- 以“结果是否成功”为判断标准过于表面,不如结合“过程是否科学”+“客观条件是否达到”
- 如何检查内存泄露问题