基于Android的小区信息获取流程分析

目录

1. 概述

2.流程分析

2.1 类图

2.2.1 AIDL服务分析

2.2.2 API接口

2.2 时序图

3. 接口说明

4总结


1. 概述

最近客户反馈使用我们的RIL库进行基站定位失败。这个FAE提需求过来时,就比较奇怪,基站定位这个是GNSS框架,跟RIL库没有关系,对比竟品说是正常。最后看log才清楚是有RIL消息获取小区注册信息,应用通过获取小区信息,客户那边进行基站定位。RIL这边只需要处理小区信息查询的消息就行。

2.流程分析

2.1 类图

在画完类图时,个人比较喜欢把类图的层次进行整理,这样就比较好理解。

从上图来看获取小区信息是Andorid典型的AIDL框架,也就是通过AIDL提供接口。

相关类的路径及功能说明如下:

  • PhoneApp.java: packages\services\Telephony\src\com\android\phone

          TeleService启动的入口类。

  • TelephonyGlobals.java: packages\services\Telephony\src\com\android\services\telephony

          Telephony相关类的创建。

  • PhoneInterfaceManager.java: packages\services\Telephony\src\com\android\phone

          phone的AIDL服务

  • PhoneFactory.java: frameworks\opt\telephony\src\java\com\android\internal\telephony

          Phone的工厂类

  • TelephonyComponentFactory.java: frameworks\opt\telephony\src\java\com\android\internal\telephony

          TelephonyComponent的工厂类

  •         GsmCdmaPhone.java: frameworks\opt\telephony\src\java\com\android\internal\telephony

          GsmCdma 电话管理类

  • ServiceStateTracker.java: frameworks\opt\telephony\src\java\com\android\internal\telephony

          服务状态跟踪

  • RIL.java: frameworks\opt\telephony\src\java\com\android\internal\telephony

          Radio interface layer

  • TelephonyManager.java: frameworks\base\telephony\java\android\telephony

          对外提供telephony接口

  • SystemServiceRegistry.java: frameworks\base\core\java\android\app

          服务注册类

2.2.1 AIDL服务分析

服务是对外提供接口,关于小区信息获取,对外提供的接口是getCellLocation函数。而服务里面是如何实现这个函数的,先从初始化来讲。

TeleService.apk是个后台服务,从manifest配置里面可看到初始化的java文件是PhoneApp,然后调用onCreate函数:

public void onCreate() {  
    if (UserHandle.myUserId() == 0) {  
        // We are running as the primary user, so should bring up the  
        // global phone state.  
        mPhoneGlobals = new PhoneGlobals(this);  
        mPhoneGlobals.onCreate();  
  
        mTelephonyGlobals = new TelephonyGlobals(this);  
        mTelephonyGlobals.onCreate();  
    }  
}  

接着调用PhoneGlobals的onCreate函数,然后创建PhoneInterfaceManager对象phoneMgr = PhoneInterfaceManager.init(this, PhoneFactory.getDefaultPhone());  

PhoneInterfaceManager类是继承了ITelephony.Stub类,所以这个是AIDL的服务,对外提供了小区信息的接口。对应的初始化代码如下:

/** 
   * Initialize the singleton PhoneInterfaceManager instance. 
   * This is only done once, at startup, from PhoneApp.onCreate(). 
   */  
  /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone) {  
      synchronized (PhoneInterfaceManager.class) {  
          if (sInstance == null) {  
              sInstance = new PhoneInterfaceManager(app, phone);  
          } else {  
              Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);  
          }  
          return sInstance;  
      }  
  }  
  
  /** Private constructor; @see init() */  
  private PhoneInterfaceManager(PhoneGlobals app, Phone phone) {  
      mApp = app;  
      mPhone = phone;  
      mCM = PhoneGlobals.getInstance().mCM;  
      mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);  
      mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);  
      mMainThreadHandler = new MainThreadHandler();  
      mTelephonySharedPreferences =  
              PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());  
      mSubscriptionController = SubscriptionController.getInstance();  
      mNetworkScanRequestTracker = new NetworkScanRequestTracker();  
  
      publish();  
  }  
  
  private void publish() {  
      if (DBG) log("publish: " + this);  
  
      ServiceManager.addService("phone", this);  
  }  

代码一路初始化下来,最终调用publish,把当前服务添加到ServiceManager里面,这样在多进程中调用此服务提供的接口就方便了,TelephonyManager类就是个很好的例子。接下来看下getCellLocation的实现流程。

@Override  
    public Bundle getCellLocation(String callingPackage) {  
        mPhone.getContext().getSystemService(AppOpsManager.class)  
                .checkPackage(Binder.getCallingUid(), callingPackage);  
        if (!LocationAccessPolicy.canAccessCellLocation(mPhone.getContext(),  
                callingPackage, Binder.getCallingUid(), Binder.getCallingPid(), true)) {  
            return null;  
        }  
  
        if (DBG_LOC) log("getCellLocation: is active user");  
        Bundle data = new Bundle();  
        Phone phone = getPhone(mSubscriptionController.getDefaultDataSubId());  
        if (phone == null) {  
            return null;  
        }  
  
        WorkSource workSource = getWorkSource(Binder.getCallingUid());  
        phone.getCellLocation(workSource).fillInNotifierBundle(data);  
        return data;  
    }  

这里就是代码一步一步的跟下去,调用的类依次是GsmCdmaPhoneà ServiceStateTrackerà RIL。最终在RIL.java里面调用如下函数,并发送RIL_REQUEST_GET_CELL_INFO_LIST消息给rild来获取。

public void getCellInfoList(Message result, WorkSource workSource) {  
        workSource = getDeafultWorkSourceIfInvalid(workSource);  
        IRadio radioProxy = getRadioProxy(result);  
        if (radioProxy != null) {  
            RILRequest rr = obtainRequest(RIL_REQUEST_GET_CELL_INFO_LIST, result,  
                    workSource);  
  
            if (RILJ_LOGD) {  
                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));  
            }  
  
            try {  
                radioProxy.getCellInfoList(rr.mSerial);  
            } catch (RemoteException | RuntimeException e) {  
                handleRadioProxyExceptionForRR(rr, "getCellInfoList", e);  
            }  
        }  
    } 

2.2.2 API接口

从类图来看,TelephonyManager是调用AIDL接口的应用层,这个角度是从AIDL框架理解是没错。但如果从应用角度来看的话,TelephonyManager又是一个提供接口的服务类,因为应用调用Telephony相关接口都是由他来提供。而这里面又是怎么实现。先看下TelephonyManager的初始化。

在SystemServiceRegistry类里进行了注册,此机制应用如果要调用TelephonyManager接口,通过getSystemService函数就行,所以此时TelephonyManager就是个服务类。

registerService(Context.TELEPHONY_SERVICE, TelephonyManager.class,  
        new CachedServiceFetcher<TelephonyManager>() {  
    @Override  
    public TelephonyManager createService(ContextImpl ctx) {  
        return new TelephonyManager(ctx.getOuterContext());  
    }});  

而接下看下TelephonyManager关于getCellLocation的封装,他其实是获取ITelephony 的客户端,然后调用ITelephony 提供的接口getCellLocation。所以对于应用角度来看,TelephonyManager就是个服务。

public CellLocation getCellLocation() {  
    android.util.SeempLog.record(49);  
    try {  
        ITelephony telephony = getITelephony();  
        if (telephony == null) {  
            Rlog.d(TAG, "getCellLocation returning null because telephony is null");  
            return null;  
        }  
        Bundle bundle = telephony.getCellLocation(mContext.getOpPackageName());  
        ......  
    }  
}  

综上所述,其实TelephonyManager类似一个代理,他不实现具体功能,帮应用间接提供接口。其实为何这么做,继续看下下面的这段代码就清楚了。TelephonyManager里面有如下4个获取AIDL客户端的函数,这么做就是让应用层少涉及业务层的逻辑,直接封装一个类给应用层处理。

/** 
 * @hide 
 */  
 private ITelephony getITelephony() {  
     return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));  
 }  
  
 /** 
 * @hide 
 */  
 private ITelecomService getTelecomService() {  
     return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));  
 }  
  
 private ITelephonyRegistry getTelephonyRegistry() {  
     return ITelephonyRegistry.Stub.asInterface(ServiceManager.getService("telephony.registry"));  
 }  
  
/** 
 * @hide 
 */  
 private IPhoneSubInfo getSubscriberInfo() {  
     // get it each time because that process crashes a lot  
     return IPhoneSubInfo.Stub.asInterface(ServiceManager.getService("iphonesubinfo"));  
 }  

2.2 时序图

关于getCellLocation的调用流程,在类图章节已分析过,这里画了个时序图做了总结。

3. 接口说明

RIL消息:RIL_REQUEST_GET_CELL_INFO_LIST

请求参数:无

返回参数:

typedef struct {

  RIL_CellInfoType  cellInfoType;   /* cell type for selecting from union CellInfo */

  int               registered;     /* !0 if this cell is registered 0 if not registered */

  RIL_TimeStampType timeStampType;  /* type of time stamp represented by timeStamp */

  uint64_t          timeStamp;      /* Time in nanos as returned by ril_nano_time */

  union {

    RIL_CellInfoGsm_v12     gsm;

    RIL_CellInfoCdma        cdma;

    RIL_CellInfoLte_v12     lte;

    RIL_CellInfoWcdma_v12   wcdma;

    RIL_CellInfoTdscdma     tdscdma;

  } CellInfo;

} RIL_CellInfo_v12;

具体查看ril.h文件

4总结

本文档其实关于小区信息的获取跟踪并不难,重点是google的设计逻辑,多看,多学习。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值